
Tämä esimerkki luo verkkosivuston Node.js:n avulla loogisen verkkosivuston toiminnan tarjoamiseksi. Express.js-kehyksen avulla verkkosivusto toteutetaan verkkosovelluksena, jossa on looginen reititys verkkosivuston muihin osiin.
HTML ja CSS perustuvat responsiiviseen verkkosivustomme suunnitteluun käyttämällä CSS Gridiä ja Flexboxia. HTML on muotoiltu uudelleen malliksi, joten asettelukoodia voidaan käyttää uudelleen lisättäessä uusia sivuja.
Asenna Node
Node.js, jota kutsutaan myös Nodeksi, on ajonaikainen ympäristö palvelinpuolen sovellusten kirjoittamiseen JavaScriptillä.
Jos Node on jo asennettu tietokoneellesi, voit ohittaa tämän osion ja jatkaa uuden Express-sovelluksen luomista.
Lataa Node-asennusohjelma viralliselta Node.js-lataussivustolta. Valitse käyttöjärjestelmällesi LTS (long term support) -versio.
Windows ja macOS
Avaa ja suorita Node-asennusohjelma (.msi Windowsissa, .pkg macOS:ssä).
Valitse Windowsissa asennusnäytössä Tools for Native Modules -valintaruutu Asenna tarvittavat työkalut automaattisesti.
Linux
Linux-järjestelmissä voit asentaa Noden paketinhallinnan avulla, asentaa käännetyt binaarit manuaalisesti tai rakentaa Noden lähdekoodista. Lisätietoja on virallisessa Node.js-asennuswikissä.
Kaikki käyttöjärjestelmät
Kun asennus on valmis, avaa pääte- tai komentokehoteikkuna. Suorita seuraava komento päivittääksesi npm, Node-pakettien hallinta. -g (global) -kytkin määrittää, että ohjelmisto on asennettu koko järjestelmän, ei vain nykyisen Node-sovelluksen.
Windows
npm install -g npm
Linux ja macOS
sudo npm install -g npm
Asenna lopuksi pikageneraattorisovellus maailmanlaajuisesti npm:n avulla.
Windows
npm install -g express-generator
Linux ja macOS
sudo npm install -g express-generator
Luo uusi Express-sovellus
Luo uusi Express.js-sovellus pääte- tai komentokehoteikkunassa. Esimerkissämme sovelluksen nimi on myapp ja näkymämoottoriksi on määritetty mopsi.
express myapp --view="pug"
Vaihda hakemisto uuteen Express-sovellukseen.
cd myapp
Lataa ja asenna tarvittavat riippuvuudet Express-sovellushakemistossa npm install -komennolla, jotka on lueteltu package.json-tiedostossa.
npm install
Jos asennetuille riippuvuuksille on saatavilla tietoturvapäivityksiä, näyttöön tulee ilmoitus.
found 1 low severity vulnerability run `npm audit fix` to fix them, or `npm audit` for details
Jos näin on, asenna tietoturvapäivitykset.
npm audit fix
Asenna nodemon
Asenna nodemon Express-sovellushakemistoon. Vaihtoehto –save-dev osoittaa, että nodemon on kehitysriippuvuus. Sitä ei käytetä itse sovelluksessa, vaan se on kehitystyön aikana käytetty työkalu.
npm install --save-dev nodemon
Lisää kehityskäynnistysskripti
Kehityksen käynnistysskripti tarjoaa tavan käynnistää verkkosovelluksesi vaihtoehdoilla, jotka auttavat sinua kehittämään sovellusta, kuten monisanaiset virheilmoitukset.
Avaa tekstieditorissa tiedosto package.json sovellushakemistossa. Tämä JSON-tiedosto määrittää Node-sovelluksesi käyttämät riippuvuudet. Lisäksi se sisältää nimettyjä käynnistysskriptejä, jotka käynnistävät sovelluksen eri tavoin.
Etsi paketti.json-tiedostosta “scripts”-merkintä. Oletusarvoisesti se sisältää vain yhden skriptin (“aloitus”).
"scripts": {
"start": "node ./bin/www"
},
Lisää uusi rivi, joka määrittää skriptin devstart seuraavasti.
Linux ja macOS
"scripts": {
"start": "node ./bin/www",
"devstart": "DEBUG=myapp:* nodemon ./bin/www"
},
Windows
"scripts": {
"start": "node ./bin/www",
"devstart": "SET DEBUG=myapp:* & nodemon ./bin/www"
},
Nämä komentosarjat (“start” ja “devstart”) voidaan suorittaa suorittamalla komento npm run scriptname.
Komento npm run devstart käynnistää sovelluksen kahden lisäkehitysominaisuuden ollessa käytössä.
- DEBUG-ympäristömuuttuja on asetettu ja määrittää, että konsolin loki- ja virhesivut, kuten HTTP 404, näyttävät lisätietoja, kuten pinojäljityksen.
- Lisäksi nodemon valvoo tiettyjä tärkeitä verkkosivuston tiedostoja. Jos muokkaat näitä tiedostoja, kuten suunnittelet sivua uudelleen tai muokkaat staattista sisältöä, nodemon käynnistää palvelimen automaattisesti uudelleen muutosten mukaan.
Käynnistä verkkopalvelin kehitystilassa.
npm run devstart
Jos Windowsin palomuuri estää verkkopalvelinsovelluksen, napsauta Salli käyttö.
Esikatsele verkkosovellusta
Kun sovellus on käynnissä, tietokoneesi toimii verkkopalvelimena, joka palvelee HTTP:tä portissa 3000.
Voit esikatsella verkkosivustoa avaamalla verkkoselain osoitteeseen localhost:3000.

Mikä tahansa paikalliseen verkkoon kytketty laite voi tarkastella sovellusta osoitteessa ipaddress:3000, jossa ip-osoite on sovellusta käyttävän tietokoneen paikallinen IP-osoite.
Jos et ole varma, mikä tietokoneen paikallinen IP-osoite on, katso: Kuinka löydän IP-osoitteeni.
Jos haluat esikatsella verkkosivustoa mobiililaitteella, yhdistä sen Wi-Fi paikallisverkkoon ja avaa osoite selaimessa.

HTML-malleja
Esimerkkimme käyttää CSS:ää, JavaScriptiä ja HTML:ää kohdasta kuinka luodaan responsiivinen verkkosivusto käyttämällä CSS Gridiä ja Flexboxia. CSS:ää ja JavaScriptiä käytetään sanatarkasti. HTML on muutettu mallikieleksi.
Mallikielellä asettelukoodi kirjoitetaan vain kerran, ja muut sivut perivät sen.
Ohjelmistoa, joka muuntaa mallin lopulliseen muotoonsa, kutsutaan malliprosessoriksi. HTML:n yhteydessä malliprosessoria kutsutaan näkymämoottoriksi.
Express.js tukee useita näkymäkoneita, mukaan lukien Mopsi.
Yleiskatsaus Mopsiin
Mopsi-kieli kuvaa HTML-dokumentteja tavalla, joka tarjoaa etuja ja lisäominaisuuksia. Mopsitiedostot renderöidään HTML-muotoon, kun käyttäjä sitä pyytää.
Mopsin kielen syntaksi poistaa tarpeen sulkea tunnisteita tai sulkea suluissa. Se tukee myös perittyjä malleja, iteraatiota, ehtoja ja JavaScript-arviointia.
Esimerkki HTML-muunnuksesta mopsiksi
Nämä ovat HTML-koodin ensimmäiset rivit, jotka käsittelevät responsiivisen verkkosivuston luomista CSS-ruudukon ja Flexboxin avulla.
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="utf-8">
<title>Title</title>
<link rel="stylesheet" href="index.css">
<script src="index.js"></script>
</head>
<body>
<div id="menu">
<section id="menuitems">
<div class="menubutton">
<h1 onclick="menuToggle('hide')" class="menubutton">☰</h1>
Mopsissa sama HTML voidaan kirjoittaa näin.
doctype html
html
head
meta(name="viewport" content="width=device-width, initial-scale=1")
meta(charset="utf-8")
title Title
link(rel="stylesheet", href="index.css")
script(src="index.js")
body
#menu
section#menuitems
.menubutton
h1.menubutton(onclick="menuToggle('hide')") ☰
Elementtitunnisteet kirjoitetaan ilman sulkeita. Lapsielementit on sisennetty. Sisennystaso määrittää elementin laajuuden, joten sulkevia tunnisteita ei tarvita.
CSS-valitsimet “id” ja “class” voidaan kirjoittaa muotoihin element#id, element.class, element#id.class jne. Jos elementtiä ei ole määritetty, elementin oletetaan olevan div. Esimerkiksi
Elementin nimen ja sen valitsimien jälkeen voidaan määrittää attribuutit suluissa pilkuilla eroteltuna luettelona. Esimerkiksi:
HTML
<div class="button" onmouseover="glow()" onclick="start()">
Mopsi
.button(onmouseover="glow()", onclick="start()")
Useiden elementtien luettelointi yhdellä rivillä
Jos elementin jälkeen on kaksoispiste (:), sitä voi seurata samalla rivillä oleva lapsielementti. Seuraavat kaksi Mopsin osaa tuottavat saman HTML-tulosteen.
a(href="/home") p Home
a(href="https://www.computerhope.com/home"): p Home
Molemmat yllä mainitut hahmonnetaan seuraavaan HTML-koodiin.
<a href="https://www.computerhope.com/home"><p>Home</p></a>
JavaScriptin arviointi
Jos elementtiä seuraa yhtäläisyysmerkki (=), kaikki tällä rivillä seuraava tulkitaan puskuroiduksi koodiksi. Koodi arvioidaan JavaScriptiksi ja tulos “puskuroidaan” (sisältyy elementin sisältöön). Yksinkertaisimmassa muodossaan puskuroitu koodi voi olla sovelluksen välittämän muuttujan nimi.
Esimerkiksi kotisivun sovellusreititin index.js välittää muuttujan otsikon arvolla “Our Farm Stand” metodille express.Router(), joka välittää sen Mopsille. Kun Mopsi tekee layout.pug, seuraava rivi:
title= pagetitle
…tulkitaan näin:
title Our Farm Stand
…joka hahmonnetaan seuraavana HTML-koodina:
<title>Our Farm Stand</title>
Mallin perintö
Mopsi-asiakirjat voivat periä muita Mopsi-asiakirjoja käyttämällä avainsanoja extends ja block.
Voit esimerkiksi luoda verkkosivuston perusasettelun, layout.pug, jossa on sivun jaetut elementit.
doctype html
html
head
title Page Title
body
p Content
block foo
Block foo -lauseessa sanotaan “lisää tähän sisältölohko nimeltä foo, joka on määritetty toisessa Mopsi-asiakirjassa, joka perii tämän mallin.”
Asiakirjojen, jotka perivät layout.pug-tiedoston, on alettava käskyllä extends layout, ja niissä on oltava block foo -lause ylimmällä sisennystasolla (uuden rivin alussa). Tämän “block foo” -lauseen lapsia lisätään malliin vastaavan lohkon sijaintiin.
Mopsi-asiakirja voi periä layout.pug-tiedoston seuraavasti.
extends layout block foo p This is the home page.
Kun asiakirja renderöidään, Pug-moottori lataa tiedoston layout.pug. Sivuston layout.pug rivilohko foo korvataan p:llä Tämä on kotisivu.
Yleiskatsaus oletusarvoiseen Express-sovellukseen
Express-sovelluksen oletusrakenne on lueteltu tässä ja kuvaukset jokaisesta tiedostosta ja hakemistosta.
myapp/ (Contains the entire Express app) ├─ app.js The core logic of the Express app. ├─ bin/ (Contains the app's executable scripts) │ └─ www A wrapper that runs app.js. ├─ node_modules/ (Contains dependencies installed by npm) ├─ package-lock.json JSON manifest of installed dependencies. ├─ package.json JSON of dependencies and config specific to your app. ├─ public/ (Files downloaded by the user's web browser) │ ├─ images/ (Contains client-accessible image files) │ ├─ javascripts/ (Contains client-accessible JavaScript files) │ └─ stylesheets/ (Contains client-accessible CSS) │ └─ style.css The site's CSS stylesheet. ├─ routes/ (Contains logic for individual site routes) │ ├─ index.js Logic of the "index" route (/). │ └─ users.js Logic of the "users" route (/users). └─ views/ (Contains HTML templates) ├─ error.pug View displayed for error pages, such as HTML 404. ├─ index.pug View displayed for the site root (/). └─ layout.pug View template of layout shared by all pages.
Sivuston ydintoiminnot on määritelty app.js:ssa. Reitit nimetään ja määritetään tässä tiedostossa.
Reitti on sivu tai sivuston osa, jonka URL-osoitteessa on yksilöllinen polku, kuten www.example.com/search, www.example.com/login jne. Nämä reitit on nimetty ja liitetty reittilogiikkaskripteihin, sovelluksessa app.js.
Reittilogiikkaskriptit tallennetaan reitit-kansioon. Kun käyttäjä pyytää reittiä, sen reittilogiikkakomentosarja käsittelee HTTP-pyyntötiedot ja lähettää vastauksen.
Näkymät-kansio sisältää HTML-malleja, joita kutsutaan näkymiksi ja joita näkymäkone (Pug) käsittelee.
Toteutus: JavaScript, CSS ja Mopsi
Seuraava koodi toteuttaa Express-verkkosovelluksen.
Sovelluksen tiedostorakenne
myapp/ ├─ app.js App core logic ├─ bin/ │ └─ www ├─ node_modules/ ├─ package-lock.json ├─ package.json ├─ public/ │ ├─ images/ │ ├─ javascripts/ │ │ └─ menu.js Implements menu toggle │ └─ stylesheets/ │ └─ style.css Stylesheet ├─ routes/ │ ├─ about.js Logic for route /about │ ├─ advice.js Logic for route /advice │ ├─ contact.js Logic for route /contact │ ├─ index.js Logic for route / │ ├─ recipes.js Logic for route /recipes │ ├─ tips.js Logic for route /tips │ └─ users.js Not used, can be deleted └─ views/ ├─ about.pug View for route /about ├─ advice.pug View for route /advice ├─ contact.pug View for route /contact ├─ error.pug ├─ index.pug View for route / ├─ layout.pug View template shared by all pages ├─ recipes.pug View for route /recipes └─ tips.pug View for route /tips blue = modified, green = new, red = not used
myapp/app.js
Sovelluksen ydinlogiikka on olennaisesti sama kuin oletusarvoisessa Express-sovelluksessa, ja lisäreitit on määritetty. “Käyttäjien” reitti poistetaan.
// core dependencies
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
// create route objectsvar indexRouter = require('./routes/index');var aboutRouter = require('./routes/about');var contactRouter = require('./routes/contact');var tipsRouter = require('./routes/tips');var recipesRouter = require('./routes/recipes');var adviceRouter = require('./routes/advice');
// the app object
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
// app config
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
// tell the app to use these routesapp.use("https://www.computerhope.com/", indexRouter);app.use('/about', aboutRouter);app.use('/contact', contactRouter);app.use('/tips', tipsRouter);app.use('/recipes', recipesRouter);app.use('/advice', adviceRouter);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
// expose this app to scripts that require it, i.e. myapp/bin/www
module.exports = app;
myapp/routes/layout.pug
layout.pug-tiedosto sisältää sivun ydinasettelun, joka on jaettu kaikille sivuston sivuille. Se sisältää kaiken, mitä sivun näyttämiseen tarvitaan, paitsi mainbody-sisältöä (block mainbody).
doctype html
html
head
title= pagetitle
meta(charset="utf-8")
meta(name="viewport" content="width=device-width, initial-scale=1")
script(src="/javascripts/menu.js")
link(rel="stylesheet", href="/stylesheets/style.css")
body
#menu
section#menuitems
.menubutton
h1.menubutton(onclick="menuToggle('hide')") ☰
a(href="/")
h3.menuhead Our Farm Stand
a(href="/tips")
h3.sectrule Tips for living well
a(href="/recipes")
h3 Recipes
a(href="/advice")
h3 Homesteading advice
a(href="/about")
h3.sectrule About Us
a(href="/contact")
h3 Contact Us
#container
#header
a(href="/")
h1.logo Our Farm Stand
.headspace
h1.menubutton(onclick="menuToggle('show')") ☰
h1.placeholder ☰
h2.navitem
a(href="/about")
.clickable-area About Us
h2.navitem
a(href="/contact")
.clickable-area Contact Us
#panel.left
section#sections
.sectionlink
a(href="/tips")
.clickable-area Tips for living well
.sectionlink
a(href="/recipes")
.clickable-area Recipes
.sectionlink
a(href="/advice")
.clickable-area Homesteading advice
block mainbody
#panel.right
h3 Our friends
section#partners.tall
.partnerlink
a(href="/")
.clickable-area Green Valley Greens
.partnerlink
a(href="/")
.clickable-area Turkey Hill Farm
.partnerlink
a(href="/")
.clickable-area Burt's Maple Syrup
.partnerlink
a(href="/")
.clickable-area Only Organic Seeds
#footer
p Copyright © 2020 Alice & Bob's Farm Stand
myapp/views/index.pug
Index.pug-tiedosto laajentaa layout.pug-tiedostoa ja sisältää pääosan sisältöä reitille /.
extends layout
block mainbody
#mainbody
section.mainbodyitems
h3 Announcements
section.announcements
.announceitem
h4.title Open for business
p.date Jan. 15
p Renovations of our new storefront are complete, and we're open for business.
h3 Items for sale
section.forsaleitems
table
tr
th Item
th Description
th Price
th.qty Qty
tr
td Milk
td Good source of calcium.
td.price $2
span.perunit / half gal.
td.qty 3
tr
td Eggs
td Great for breakfast and baking.
td.price $4
span.perunit / doz.
td.qty 6
tr
td Whole chicken
td Perfect for roasting.
td.price $5
span.perunit / lb.
td.qty 4
h3 Upcoming events
section
.eventitem
h4.title Cider Fest
p.date October 20, 2pm–6pm
p Celebrate the season with fresh-pressed cider from our orchards.
.eventitem
h4.title Bread baking workshop
p.date December 13, 9am–noon
p Learn how to create and cultivate a sourdough starter.
h3 Message of the day
section
.motditem
p Eat better food. Support your local farm stand.
h3#partners.wide Our friends
section#partners.wide
.partnerlink.wide
a(href="")
.clickable-area Green Valley Greens
.partnerlink.wide
a(href="")
.clickable-area Turkey Hill Farm
.partnerlink.wide
a(href="/")
.clickable-area Burt's Maple Syrup
.partnerlink.wide
a(href="")
.clickable-area Only Organic Seeds
.bodyspace
myapp/routes/index.js
Tiedosto index.js sisältää logiikkaa reitille /.
var express = require('express');
var router = express.Router();
router.get("https://www.computerhope.com/", function(req, res, next) {
res.render('index', { pagetitle: 'Our Farm Stand' });
});
module.exports = router;
myapp/public/javascripts/menu.js
Tiedosto menu.js sisältää JavaScriptin Grid- ja Flexbox-esimerkistä. Se toteuttaa valikon vaihtotoiminnon.
function menuToggle(state) {
var ele = document.getElementById('menu');
switch(state) {
case 'show':
ele.style.opacity=1;
ele.style.color="rgb(96, 96, 96)";
ele.style.visibility='visible';
ele.style.transition='visibility 0s, opacity 0.3s';
break;
case 'hide':
ele.style.opacity=0;
ele.style.color="black";
ele.style.visibility='hidden';
ele.style.transition='visibility 0.3s, opacity 0.3s';
break;
}
}
myapp/public/stylesheets/style.css
Tiedosto style.css sisältää CSS:n Grid- ja Flexbox-esimerkistä.
/* element styles */
* {
margin: 0; /* by default, all elements (selector *) have no margin */
}
html {
width: 100%; /* 100% width of parent (root) element */
height: 100vh; /* 100% height of viewport */
background: rgb(0, 0, 0, 0.1); /* 10% black */
font-size: 1.0em; /* our root font size */
font-family: Arial, Helvetica, sans-serif; /* default font */
}
body {
min-height: 100%;
}
section {
padding: 0.5rem;
flex-grow: 1; /* in a flexbox, sections expand along flex axis */
}
h1 { /* Website name in header */
font-size: 2.0rem;
font-weight: normal;
}
h2 { /* About, Contact */
font-size: 1.25rem;
}
h3 { /* Section headings */
font-size: 1.2rem;
padding: 0.5rem;
}
h4 { /* Section item title */
font-weight: normal;
padding: 0.5rem;
}
p { /* Section item body */
padding: 0.5rem;
}
a:link, a:visited { /* anchor links, and visited anchor links */
color: black;
text-decoration: none; /* disable underline */
}
a:hover { /* when anchor link is hovered */
color: rgb(25, 25, 25);
}
a:active { /* when anchor link is clicked */
color: rgb(96, 96, 96);
}
/* component styles */
#container {
display: grid;
height: 100vh;
grid-template-columns:
[left] 10rem auto 10rem [right];
grid-template-rows:
[top] 5rem auto 5rem [bottom]; /* header height fits its content */
grid-template-areas:
"head head head"
"panleft mainbody panright"
"foot foot foot";
}
#header {
grid-area: head; /* corresponds to name in template */
background: rgb(0, 0, 0, 0.2); /* 20% black */
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: baseline; /* site name and nav item text aligns baseline */
padding: 1.0rem;
}
#panel { /* for element id="panel" */
display: flex; /* this element is a flexbox parent */
flex-direction: column; /* its child elements flex vertically */
padding: 0.5rem;
background: rgb(0, 0, 0, 0.1); /* 10% black */
}
#panel.left { /* for element id="panel" and class="left" */
grid-area: panleft; /* this element fills a grid area */
}
#panel.right {
grid-area: panright;
}
#footer {
grid-area: foot;
display: flex; /* this element is a flexbox parent */
flex-direction: column; /* its child elements flex vertically */
justify-content: center; /* horizontal center footer content */
align-items: center; /* vertical center footer content */
padding: 0.5rem;
background: rgb(0, 0, 0, 0.2);
}
#mainbody { /* for element id="mainbody" */
display: flex; /* this element is a flexbox parent */
flex-direction: column; /* its child elements flex vertically */
grid-area: mainbody;
justify-self: center; /* fixed-width mainbody always centered */
width: 100%;
min-width: 22.5rem; /* mainbody width can't go < 22.5rem */
}
div#panel,
div#mainbody { /* extra space under header */
padding-top: 0.5rem;
}
#partners, #sections { /* for element id="partners" or id="sections" */
display: flex; /* this element is a flexbox parent */
flex-direction: row; /* its child elements flex horizontally */
flex-wrap: wrap; /* its child elements can wrap to next line */
align-content: flex-start; /* child elements start in upper left */
}
#partners.wide { /* for element id="partners" and class="wide" */
display: none; /* by default, do not display this element */
}
#menu {
position: absolute; /* menu position unaffected by other elements */
right: 0; /* zero pixels from the right boundary */
background: rgb(239, 239, 239);
border: 0.15rem solid rgb(0, 0, 0, 0.4);
visibility: hidden; /* visibility property supports transitions */
opacity: 0; /* opacity + visibility transition = menu fade effect */
z-index: 1; /* ensure menu appears over all other content */
}
#menuitems { /* menu is implemented as a flexbox container */
display: flex;
flex-direction: column;
padding: 1rem;
}
#menuitems h3 {
border-top: 0.15rem solid rgb(0, 0, 0, 0.1); /* light horizontal rule */
}
#menuitems .sectrule {
border-color: rgb(0, 0, 0, 0.25); /* darker horizontal rule */
}
#menuitems .menuhead {
border-top: none;
}
#menuitems h3:hover {
background-color: rgb(0, 0, 0, 0.1); /* gray of rollover menuitems */
}
.menubutton {
text-align: right;
cursor: pointer; /* indicates it can be clicked like a link */
user-select: none; /* user cannot select the button as text */
}
#menuitems .alignright {
text-align: right; /* right-aligned menu item text (unused) */
}
#header h1.menubutton {
display: none; /* in default view (landscape), hide menu button */
border: 0.15rem solid rgb(0, 0, 0, 0); /* (invisible) alignment shim */
}
#header .placeholder { /* this invisible button is rendered when menu */
color: rgb(0, 0, 0, 0); /* button is hidden, so header height matches. */
user-select: none; /* user can't select text of invisible button */
}
.sectionlink, .partnerlink {
border-radius: 0.25rem; /* give this element a slight rounded edge */
font-weight: normal;
font-size: 1.1rem;
padding: 0.5rem;
width: 7rem; /* fixed width for these items */
margin-bottom: 1rem; /* slight margin for readability */
background: rgb(0, 0, 0, 0.1);
}
.sectionlink:hover, .partnerlink:hover {
background-color: rgb(0, 0, 0, 0.065); /* brighten bg on mouse hover */
}
.partnerlink {
height: 7rem; /* partner elements are additionally fixed height */
}
.partnerlink.wide {
margin: 0.5rem 1rem 0.5rem 0; /* margins for spacing if they wrap */
}
.clickable-area { /* use whenever a clickable area excludes margins */
height: 100%; /* clickable area spans height of parent */
}
.eventitem, .announceitem, .motditem {
margin-bottom: 0.5rem; /* slight margin for readability */
}
.title { /* e.g., "Open for business" */
font-style: italic;
font-weight: normal;
font-size: 1.1rem;
}
.date, .ingredient { /* e.g., January 1, 2021 */
font-style: italic;
font-size: 0.9rem;
padding: 0 0 0.01rem 0.5rem;
color: rgb(0, 0, 0, 0.5);
}
.navitem { /* About, Contact */
font-weight: normal;
padding: 0 0.5rem 0 1rem;
}
.headspace, .panspace, .footspace, .bodyspace {
flex-grow: 1; /* these elements expand on flex axis to fill space */
}
/* table styles ("items for sale") */
table {
border-collapse: collapse; /* pixel-adjacent table cells */
width: 100%;
margin-bottom: 1rem;
}
th {
text-align: left;
}
tr {
margin: 4rem 0 0 0;
border-bottom: 0.15rem solid rgb(0, 0, 0, 0.2); /* horizontal rule */
}
td, th {
padding: 0.5rem;
vertical-align: top;
}
td.price {
white-space: nowrap; /* white space in price does not wrap line */
}
td.qty, th.qty {
text-align: center;
}
span.perunit {
opacity: 0.5;
}
/* responsive styles applied in portrait mode */
@media screen and (max-width: 45rem) { /* if viewport width < 45rem */
#panel.left {
grid-column-end: left; /* panel grid area shrinks to nothing */
}
#panel.right {
grid-column-start: right; /* panel grid area shrinks to nothing */
}
#partners.tall {
display: none; /* hide partners in panel (overwrites display: flex) */
}
#partners.wide {
display: flex; /* show partners in body (overwrites display: none) */
}
#panel, /* these disappear from layout */
#header .placeholder,
.navitem {
display: none;
}
#mainbody {
grid-column-start: left; /* mainbody now starts at left edge */
grid-column-end: right; /* mainbody now ends at right edge */
}
#header h1.menubutton { /* display the header menu button */
display: inline; /* overwrites display: none */
}
}
Toissijaiset reitit
Seuraavat tiedostot sisältävät logiikan toissijaisille reiteille — Tietoja, neuvoja, yhteystietoja jne.
myapp/routes/about.js
var express = require('express');
var router = express.Router();
router.get("https://www.computerhope.com/", function(req, res, next) {
res.render('about', { pagetitle: 'About Us' });
});
module.exports = router;
myapp/routes/advice.js
var express = require('express');
var router = express.Router();
router.get("https://www.computerhope.com/", function(req, res, next) {
res.render('advice', { pagetitle: 'Homesteading Advice' });
});
module.exports = router;
myapp/routes/contact.js
var express = require('express');
var router = express.Router();
router.get("https://www.computerhope.com/", function(req, res, next) {
res.render('contact', { pagetitle: 'Contact Us' });
});
module.exports = router;
myapp/routes/recipes.js
var express = require('express');
var router = express.Router();
router.get("https://www.computerhope.com/", function(req, res, next) {
res.render('recipes', { pagetitle: 'Recipes' });
});
module.exports = router;
myapp/routes/tips.js
var express = require('express');
var router = express.Router();
router.get("https://www.computerhope.com/", function(req, res, next) {
res.render('tips', { pagetitle: 'Tips For Living Well' });
});
module.exports = router;
Toissijaiset näkymät
Seuraavat näkymät perivät layout.pug.
myapp/views/about.pug
extends layout
block mainbody
#mainbody
section#mainbodyitems
p Alice & Bob have been operating their farm stand since 1992.
myapp/views/advice.pug
extends layout
block mainbody
#mainbody
section#mainbodyitems
h3 Homesteading Advice
p Never, ever stand behind a heifer.
myapp/views/contact.pug
extends layout
block mainbody
#mainbody
section#mainbodyitems
h3 Alice & Bob
p 1344 Chattanooga Way
p Homestead, VT 05401
p (802) 555-5555
myapp/views/recipes.pug
extends layout
block mainbody
#mainbody
section#mainbodyitems
h3 Alice's Recipes
p
b No-knead next-day dutch oven bread
p.ingredient 1/4 tsp active dry yeast
p.ingredient 3 cups all-purpose flour
p.ingredient 1 1/2 tsp salt
p.ingredient Cornmeal or wheat bran for dusting
p In a large bowl, dissolve yeast in water.
p Add the flour and salt, stirring until blended.
p Cover bowl. Let rest at least 8 hours, preferably 12 to 18, at warm room temperature, about 70 degrees.
p When the surface of the dough is dotted with bubbles, it's ready to be folded. Lightly flour a work surface. Sprinkle flour on the dough and fold it over on itself once or twice. Cover loosely and let it rest about 15 minutes.
p Using just enough flour to keep the dough from sticking, gently shape it into a ball. Generously coat a clean dish towel with flour, wheat bran, or cornmeal. Put the seam side of the dough on the towel. Cover with another towel and let rise for 1 to 2 hours.
p Heat oven to 475°. Cover and bake for 30 minutes.
myapp/views/tips.pug
extends layout
block mainbody
#mainbody
section#mainbodyitems
h3 Alice's Tips
p Always rise before the sun.
p Never use fake maple syrup.
p If the bear is black, be loud, attack.
p If the bear is brown, play dead, lie down.
Ulkomuoto
Muotokuvatilassa toissijaisia reittejä käytetään valikossa.

Vaakatilassa ne ovat käytettävissä otsikosta ja vasemmasta paneelista.

