Зарегистрируйтесь, чтобы продолжить обучение

Шаблоны JS: Веб-разработка

В этом уроке мы познакомимся с шаблонизаторами.

Зачем нужны шаблонизаторы

Fastify и другие подобные фреймворки позволяют создавать полноценные сайты без дополнительных инструментов. Рабочий сайт должен просто возвращать ответ с HTML-кодом, который браузер отобразит как страницу:

app.get('/', (req, res) => {
  res.type('html')
  res.send('<h1>Hello World!</h1>')
})

Здесь мы видим упрощенный пример, в котором мы возвращаем только заголовок H1.

В реальных приложениях возвращаемый HTML состоит из сотен и тысяч строк. Работать с такими объемами стандартным способом очень сложно. Вот лишь некоторые проблемы, с которыми мы столкнемся:

  • Такой код сложно формировать, редактировать и поддерживать
  • В таком коде очень легко допустить ошибку и очень сложно ее обнаружить
  • В таком коде будут возникать проблемы с одинарными или двойными кавычками, придется их экранировать и постоянно следить за этим

Для решения этой проблемы используются шаблонизаторы. Это библиотеки, которые позволяют формировать HTML в отдельных файлах с подсветкой и удобной подстановкой данных. Другими словами, мы получаем не HTML внутри кода, а код внутри HTML.

В мире Fastify есть несколько разных шаблонизаторов. Для этого курса мы выбрали Pug.

Посмотрим, как выглядит шаблон в PUG:

doctype html
html(lang='en')
 head
   title Hello, World!
 body
   h1 Hello, World!
   div.remark
     p Pug rocks!

Как начать работать с PUG

Чтобы начать работу с pug в Fastify, нужно установить два пакета:

npm i pug @fastify/view

Пакет @fastify/view добавляет в Fastify поддержку шаблонизаторов. При подключении плагина нужно указать, что мы хотим использовать Pug в качестве шаблонизатора в нашем приложении:

import fastify from 'fastify'
import view from '@fastify/view'
import pug from 'pug'

const app = fastify()
const port = 3000

// Подключаем pug через плагин
await app.register(view, { engine: { pug } })

app.listen({ port }, () => {
  console.log(`Example app listening on port ${port}`)
})

Шаблоны Pug хранятся в директории src/views и имеют расширение pug. Чтобы попрактиковаться, добавим первый шаблон для главной страницы сайта. Для этого выполним два действия:

  1. Создадим шаблон src/views/index.pug со следующим содержимым:

    doctype html
    html(lang='en')
    head
        title Hello Hexlet!
    body
        main
        h1 Hello, World!
        p Fastify + Pug
    
  2. Укажем обработчику главной страницы использовать этот шаблон:

    app.get('/', (req, res) => res.view('src/views/index'));
    

Обратите внимание на метод res.view() в коде выше. Он выполняет рендеринг указанного шаблона и добавляет результат в HTTP-ответ.

Путь до шаблона указывается относительно директории корня приложения:

# Например, структура может выглядеть так
views
├── courses
│   ├── edit.pug
│   ├── index.pug
│   ├── new.pug
│   └── show.pug
├── index.pug
└── users
    ├── edit.pug
    ├── index.pug
    └── new.pug

Сами шаблоны могут располагаться и на более глубоком уровне. Это становится важно, когда количество шаблонов увеличивается.

Шаблонизатор не задает правила именования и внутренней структуры шаблонов. Но работать без правил слишком сложно, поэтому со временем мы самостоятельно выработаем правила и будем их придерживаться.

Как работает отображение данных

Как правило, HTML внутри шаблонов формируется на основе данных, которые мы хотим вывести. Например, чтобы вывести информацию о курсе на странице /courses/:id, мы передаем объект этого курса в шаблон и формируем HTML на основе его содержимого.

Рассмотрим, по какому алгоритму обработчики обычно работают с шаблонами:

  1. Сначала они извлекают все необходимые данные
  2. Затем они создают объект с данными для шаблона
  3. В итоге они передают этот объект в шаблон в виде объекта
const state = {
  courses: [
    {
      id: 1,
      title: 'JS: Массивы',
      description: 'Курс про массивы в JavaScript',
    },
    {
      id: 2,
      title: 'JS: Функции',
      description: 'Курс про функции в JavaScript',
    },
  ],
}

app.get('/courses/:id', (req, res) => {
  const { id } = req.params
  const course = state.courses.find(({ id: courseId }) => courseId === parseInt(id))
  if (!course) {
    res.code(404).send({ message: 'Course not found' })
    return
  }
  const data = {
    course,
  }
  res.view('src/views/courses/show', data)
})

Остался последний шаг — вывести данные курса в шаблоне. Для этого в шаблонах есть доступ к объекту, который мы передали в обработчике:

doctype html
html
  head
    title Hello Hexlet!
  body
    main
      h1= course.title
      p= course.description

Какие управляющие конструкции используются в Pug

Кроме обычного вывода значений, в шаблонах часто используются условные конструкции и циклы. С их помощью показываются списки, прячутся или показываются определенные блоки и так далее.

Подробнее обо всех этих конструкциях можно прочитать в официальной документации. А здесь мы разберем пример на базе маршрута /courses, по которому выводится список курсов с описанием и ссылками на курсы.

  1. Обработчик маршрута:

    app.get('/courses', (req, res) => {
      const data = {
        courses: state.courses, // Где-то хранится список курсов
        header: 'Курсы по программированию',
      }
      res.view('src/views/courses/index', data)
    })
    
  2. Шаблон

    html
    head
        title Хекслет
    body
        h1= header
        if courses.length === 0
          p Пока не добавлено ни одного курса
        else
          each course in courses
            div
              h2
                a(href=`/courses/${course.id}`)= course.title
              p= course.description
    

Логика вывода здесь такая:

  • Если список курсов пустой, то выводится соответствующее сообщение
  • Если курсы в списке есть, то на основе этого списка формируются HTML-блоки с информацией о курсе и ссылки на его страницу

Этих конструкций хватит для решения большинства стандартных задач.


Дополнительные материалы

  1. Pug

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff