Как создать свой первый веб-сайт Node.js с помощью Express и Pug

Предпосылки

Если вы знаете JavaScript, но никогда раньше не занимались серверным программированием, это руководство для вас. Прежде чем продолжить, вам необходимо установить npmNode.js.
Вы можете поискать в Интернете инструкции по установке Node.js и npm предпочитаемой вами платформы или посетить веб- сайт Node.js ( npmпоставляется с Node). Версии, которые я использовал при создании этого проекта, следующие:
Node.js v16.15.1
npm v8.11.0
Вы можете просмотреть версию Node npm, которую вы установили, выполнив следующие команды в своем терминале:
node -v
npm -v
Я полагаю, что код все еще будет работать, даже если вы используете более старую версию Node , но если у вас возникнут проблемы с выполнением руководства, попробуйте выполнить обновление до версий, которые я использовал, чтобы посмотреть, решит ли это вашу проблему.

Что мы будем делать

Я расскажу вам, как создать простой веб-сайт с помощью Node.js, Express и Pug. Веб-сайт будет иметь домашнюю страницу и несколько других страниц, на которые мы сможем перейти.

Начиная

Загрузите начальные файлы затем выполните следующую команду из корня загруженной папки, чтобы установить зависимости проекта.
npm install
Я решил предоставить эти начальные файлы, чтобы вы не рисковали столкнуться с ошибками в результате использования версии пакета, отличной от той, которую использовал я. Не волнуйтесь, я объясню, что делает каждая зависимость, по ходу дела.
Теперь откройте server.jsв корневом каталоге и введите следующий код:

const express = require('express');
const app = express();

Мы начнем с импорта Express , который представляет собой используемую нами структуру веб-сервера. Функция express()является функцией верхнего уровня, экспортируемой expressмодулем.
Затем нам нужно настроить веб-сайт для работы на порту 7000. Вы можете выбрать другой порт, если на вашем компьютере используется порт 7000.

const server = app.listen(7000, () => {
  console.log(`Express running → PORT ${server.address().port}`);
});

Вы можете запустить веб-сервер, запустив его node server.jsиз корня папки вашего проекта.
Если вы откроете http://localhost:7000в своем браузере, вы увидите сообщение об ошибке «Cannot GET /». Это связано с тем, что мы не определили корневой маршрут для нашего веб-сайта, поэтому давайте продолжим и сделаем именно это.
Добавьте следующий код перед serverобъявлением переменной в server.js:

app.get('/', (req, res) => {
  res.send('Hello World!');
});

Приведенный выше код указывает, что при выполнении запроса GET к корню нашего веб-сайта get()будет вызываться функция обратного вызова, которую мы указали в методе. В данном случае мы отправляем текст «Hello World!»
Хотя вы можете настроить маршруты для других типов HTTP-запросов, таких как POST, PUT и им подобных, в этом руководстве мы будем рассматривать только запросы GET.
Теперь вам нужно перезагрузить сервер, прежде чем изменения вступят в силу. Делать это каждый раз, когда вы вносите изменения в свой код, может стать невероятно утомительным, но я покажу вам, как обойти это в следующем разделе.
На данный момент остановите процесс Node в своем терминале с помощью ctrl + c запустите его снова, командой node server.jsзатем обновите браузер. Вы должны увидеть текст «Hello World!» на странице.

Настройте Nodemon для автоматического перезапуска сервера приложений Node.js.

Есть несколько инструментов, которые вы можете использовать для автоматического перезапуска сервера Node после каждого изменения, чтобы вам не приходилось с этим сталкиваться. Я предпочитаю Nodemon , который очень хорошо зарекомендовал себя в моих проектах.
Если вы посмотрите на package.jsonфайл, вы увидите, что nodemonон указан в разделе devDependencies, так что вы можете сразу начать его использовать.
Измените стартовый скрипт в package.json на следующий:

{
  "scripts": {
    "start": "npx nodemon server.js"
  }
}

Остановите процесс node и запустите npm start. Теперь веб-сервер будет перезапускаться автоматически каждый раз, когда вы вносите изменения.

Отображение HTML в браузере

Вместо того, чтобы просто отправлять текст в браузер, когда кто-то выбирает маршрут, мы можем отправить HTML-код, как это делают большинство веб-сайтов. Мы можем создавать HTML-файлы вручную и указывать, какой файл отправлять в браузер после того, как запрос GET достигнет маршрута, но почти всегда лучше использовать механизм шаблонов для создания HTML-файлов на лету.
Механизм шаблонов позволяет вам определять шаблоны для вашего приложения и заменять переменные в шаблоне фактическими значениями во время выполнения при преобразовании шаблона в фактический файл HTML, который затем отправляется клиенту.
Есть несколько механизмов шаблонов, которые вы можете использовать с Express. Pug , Mustache и EJS — одни из самых популярных. Я буду использовать здесь Pug, потому что мне нравится его синтаксис, но вы можете выполнить руководство в другом механизме шаблонов, если хотите.
Я уже включил pugпакет в зависимости нашего проекта, чтобы мы могли продолжить и использовать его в экспресс-режиме.
Добавьте следующий код в свой server.jsфайл под appпеременной. Это говорит об экспрессе, который мы используем pugв качестве нашего механизма шаблонов.

app.set('view engine', 'pug');

Express ожидает, что файлы наших шаблонов будут храниться в папке с именем views. Создайте эту папку в корне каталога вашего проекта, затем создайте файл с именем index.pugв viewsпапке и вставьте в него следующий код:

p Hello Pug!

Теперь измените строку в вашем server.jsфайле res.send('Hello World!')на res.render('index'). Это говорит Express об отображении шаблона индекса, который мы только что создали. Вам не нужно ставить .pugрасширение в конце.
Если вы обновите свой браузер, вы должны увидеть слова «Hello Pug!» на странице. Если вы проверите текст с помощью инструментов разработчика вашего браузера, вы должны увидеть, что код, который вы написали, index.pugбыл преобразован в обычный HTML.

основы мопса

Первое, что нужно знать, это то, что Pug использует отступы для описания структуры шаблона, а закрывающие теги отсутствуют.
Вот основной синтаксис для Pug, который вам нужно понять, чтобы завершить это руководство, а также HTML-эквивалент в комментарии под кодом Pug.
У вас есть свой элемент, пробел и содержимое, как мы сделали выше:

p Hello Pug!
// <p>Hello Pug!</p>

Вы можете поместить содержимое элемента в отдельную строку, чтобы добиться того же результата:

p
  | Hello Pug!
// <p>Hello Pug!</p>

Если вы хотите вложить элементы, вам нужно сделать отступ на один уровень:

div
  p Hello Pug!
  button Click Me

// <div>
//   <p>Hello Pug!</p>
//   <button>Click Me</button>
// </div>

Вы можете использовать классы и идентификаторы для своих элементов следующим образом:

div.container
  p#hello Hello Pug!
  button.btn.btn-blue Click Me
// <div class="container">
//   <p id="hello">Hello Pug!</p>
//   <button class="btn btn-blue">Click Me</button>
// </div>

А вот как вы используете атрибуты HTML:

img(src="fish.png" alt="A big fish")
// <img src="fish.png" alt="A big fish">

передача переменных в pug

Вы можете передать информацию из своего маршрута в свой шаблон, передав объект при отображении шаблона следующим образом:
server.js

res.render('index', {
  name: 'Ayo'
});

И в вашем файле шаблона вы можете сослаться на него следующим образом:
index.pug

p Hello my name is #{name}
// <p>Hello my name is Ayo</p>

Если вы хотите использовать переменную в атрибуте, вы должны сделать это с помощью литералов шаблона ES2015:

img(src=`${name}.jpg` alt=`This is ${name}`)
// <img src="Ayo.jpg" alt="This is Ayo">

Давайте создадим сайт

Мы можем продемонстрировать мощь Pug и Express, создав простой веб-сайт.
Сначала создайте default.pugфайл в viewsкаталоге и вставьте в него следующее содержимое. Этот файл выступает своего рода шаблоном для других наших шаблонов.

doctype html
html
  head
    title #{title}
    link(rel='stylesheet', href='/css/style.css')
    meta(name="viewport" content="width=device-width, initial-scale=1")
  body
    main
      block header
        header.header
          h1 #{title}
      block content

Ключевое blockслово позволяет нам расширить шаблон посредством наследования. При расширении шаблона вы можете определить пользовательский контент для любого блока в родительском шаблоне.
Вот пример того, как это работает. В index.pugфайле введите следующее:

extends default
block content
  div.container

Шаблон по умолчанию ожидает переменную title, поэтому нам нужно передать ее при рендеринге любого шаблона, который ее расширяет.
server.js

res.render('index', {
  title: 'Homepage'
});

Обновите браузер, чтобы увидеть изменения.

работа со статическим контентом

Нам нужно сообщить Express, где размещаются статические файлы (такие как таблицы стилей, шрифты или изображения) для нашего веб-сайта, чтобы он знал, как правильно их обслуживать.
Измените свой server.jsфайл, чтобы он выглядел так:

// ...
app.set('view engine', 'pug');
// serve static files from the `public` folder
app.use(express.static(__dirname + '/public'));
// ... 

Если вы обновите свой браузер, стили, на которые есть ссылки, default.pugдолжны сработать.

работа с json-данными

В корневой папке находится people.jsonфайл, который мы будем использовать для построения страниц сайта. Если вы просмотрите его, вы увидите profilesключ, представляющий собой массив, содержащий несколько объектов, каждый из которых представляет профиль человека.
В реальном приложении вы, скорее всего, будете извлекать эти данные из какой-то базы данных, но этот метод должен хорошо проиллюстрировать концепцию.
Давайте создадим домашнюю страницу сайта. Нам нужно передать данные json в наш шаблон индекса и отобразить карту для каждого человека, определенного в нем.
Измените свой server.jsфайл, чтобы он выглядел так:

const express = require('express');
const people = require('./people.json');
const app = express();

app.set('view engine', 'pug');
app.use(express.static(__dirname + '/public'));
app.get('/', (req, res) => {
  res.render('index', {
    title: 'Homepage',
    people: people.profiles
  });
});

const server = app.listen(7000, () => {
  console.log(`Express running → PORT ${server.address().port}`);
});

Здесь мы сохраняем ссылку на people.jsonфайл в peopleпеременной. Далее мы передаем profilesмассив в indexшаблон в качестве peopleключа.
Теперь измените свой index.pugфайл, чтобы он выглядел так:

extends default

block content
  div.container
    each person in people
      div.person
        div.person-image(style=`background: url('/images/${person.imgSrc}') top center
        no-repeat; background-size: cover;`)
        h2.person-name
          | #{person.firstname} #{person.lastname}
        a(href=`/profile?id=${person.id}`)
          | View Profile

Ключевое eachслово в pug позволяет нам перебирать массивы и объекты. К каждому объекту в peopleмассиве можно получить доступ по personключу для каждой итерации, и мы используем это для создания карты для каждого человека на главной странице.
Внизу каждого человека есть ссылка, которая должна перейти к его соответствующим профилям. Но когда вы щелкаете по нему, вы получаете сообщение об ошибке «невозможно получить/профиль».
Давайте исправим это, создав новый маршрут для /profile. В server.js, добавьте следующий код под корневой маршрут:

app.get('/profile', (req, res) => {
  res.send(req.query.id);
});

Как только вы нажмете на профиль человека, он должен отправить идентификатор человека обратно в браузер. В Express вы можете получить доступ к параметрам запроса URL в разделе req.query.
Давайте создадим новый файл шаблона, который будет отображаться, как только кто-то попадет на маршрут профиля. Идите вперед и создайте profile.pugв viewsпапке и добавьте следующее содержимое:

extends default

block header

block content
  div.profile
    div.profile-image(style=`background: url('/images/${person.imgSrc}') top center
    no-repeat; background-size: cover;`)
    div.profile-details
      h1.profile-name
        | #{person.firstname} #{person.lastname}
      h2.profile-tagline
        | #{person.tagline}
      p.profile-bio
        | #{person.bio}
      a.button.button-twitter(href=`${person.twitter}`)
        | Follow me on Twitter

Здесь мы снова расширяем шаблон по умолчанию и заменяем headerблок, определенный внутри, пустым блоком, потому что мы не хотим, чтобы содержимое заголовка по умолчанию отображалось на этой странице.
В contentблок я добавил необходимую разметку для каждого профиля. Как видите, этот шаблон ожидает, что мы передадим объект, описывающий каждого человека, так что давайте сделаем именно это.
Измените /profileмаршрут, чтобы он выглядел так:

app.get('/profile', (req, res) => {
  const person = people.profiles.find(p => p.id === req.query.id);
  res.render('profile', {
    title: `About ${person.firstname} ${person.lastname}`,
    person,
  });
});

Сначала мы используем метод массива find()для извлечения первого объекта, idсвойство которого соответствует полученному в параметрах запроса. Затем мы передаем этот объект в profileшаблон.
В результате профиль каждого человека можно просмотреть, щелкнув соответствующие ссылки профиля на главной странице.

вот итоговый код наших файлов

server.js

const express = require("express");
const people = require("./people.json");
const app = express();

app.set("view engine", "pug");
// serve static files from the `public` folder
app.use(express.static(__dirname + "/public"));
app.get("/", (req, res) => {
  res.render("index", {
    title: "Homepage",
    people: people.profiles
  });
});

app.get("/profile", (req, res) => {
  const person = people.profiles.find(p => p.id === req.query.id);
  res.render("profile", {
    title: `About ${person.firstname} ${person.lastname}`,
    person
  });
});

const server = app.listen(7000, () => {
  console.log(`Express running → PORT ${server.address().port}`);
});

index.pug

extends default
block content
  div.container
    each person in people
      div.person
        div.person-image(style=`background: url('/images/${person.imgSrc}') top center
        no-repeat; background-size: cover;`)
        h2.person-name
          | #{person.firstname} #{person.lastname}
        a(href=`/profile?id=${person.id}`)
          | View Profile

default.pug

doctype html
html
  head
    title #{title}
    link(rel='stylesheet', href='/css/style.css')
    meta(name="viewport" content="width=device-width, initial-scale=1")
  body
    main
      block header
        header.header
          h1 #{title}
      block content

profile.pug

extends default
block header
block content
  div.profile
    div.profile-image(style=`background: url('/images/${person.imgSrc}') top center
    no-repeat; background-size: cover;`)
    div.profile-details
      h1.profile-name
        | #{person.firstname} #{person.lastname}
      h2.profile-tagline
        | #{person.tagline}
      p.profile-bio
        | #{person.bio}
      a.button.button-twitter(href=`${person.twitter}`)
        | Follow me on Twitter

можете скачать готовый проект.
не забудьте прописать команду npm i

Скачать

Обсуждение закрыто.