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

Лейауты в Pug JS: Веб-разработка

Использование шаблонов значительно упрощает создание HTML-страниц на стороне сервера. Но у этого подхода есть и недостатки. Он порождает определенные неудобства, связанные с повторяющимися блоками. В этом уроке мы поговорим об этой проблеме и выясним, как предотвращать такие ситуации.

Инвертированный подход к повторяющимся блокам

Представьте себе типовой сайт. На большинстве сайтов есть верхнее меню, футер и еще немало повторяющихся блоков. Как с ними правильно работать? Кажется, что копировать все это в каждый шаблон — не самая лучшая идея.

Есть еще один подход — вынести общие куски в отдельные файлы и включить их в нужных шаблонах:

<!-- Пример шаблонизатора JSP -->
<%@ include file="parts/header.jsp" %>
<!-- Здесь код конкретного шаблона -->
<%@ include file="parts/footer.jsp" %>

Это простое решение, которые использовалось в самых ранних шаблонизаторах. Разработчики быстро поняли, насколько это неудобно. Проблема заключается в двух аспектах:

  • Разработчику приходится делать подобные включения абсолютно в каждом шаблоне вручную. В нашем примере их всего два. А что, если бы их были сотни? На работу с каждым отдельным шаблоном ушло бы очень много времени
  • Разработчику приходится вручную проверять разные файлы и искать незакрытые теги, потому что части одних и тех же тегов могут быть разбросаны по разным файлам. Отлаживать такие проекты очень сложно

Эти проблемы решил инвертированный подход, в котором не куски сайта вставляются в шаблон, а шаблон вставляется в макет сайта. Принцип здесь такой:

  • В одном месте описываем макет – основу сайта, которая не меняется по структуре
  • Шаблонизатор делает так, чтобы шаблон конкретной страницы вставлялся в этот макет

В этом случае шаблон ничего не знает об устройстве макета, а макет хранит все свои части в одном месте.

Инвертированный подход в PUG

Шаблонизатор PUG поддерживает работу с макетами. Посмотрим, как это работает. Для начала создадим макет src/views/layouts/page.pug:

//- src/views/layouts/page.pug
html
  head
    meta(charset='utf-8')
    meta(name='viewport', content='width=device-width, initial-scale=1')
  body
    p
      a(href='/courses')
      block content

Обратим внимание на content — сюда PUG поместит шаблон конкретной страницы. Посмотрим, как выглядит шаблон списка курсов:

//- src/views/courses/index.pug
extends ../layouts/page.pug

block content
  a(href='/courses/new') New Course

  each course in courses
    div
      #{course.title}

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

Главная особенность в том, что мы помещаем содержимое шаблона в специальную конструкцию, которая вставляет его в нужное место в макет:

block content

Может показаться, что это избыточно. С другой стороны, создатели шаблонизатора хотели сделать чуть более продвинутую систему, чем подстановку в один блок. Такая конструкция позволяет вставлять в макет сразу несколько блоков и передавать туда данные конкретного шаблона. Посмотрим на пример такого макета и шаблона:

//- page.pug
html
  head
    title My Site
  body
    block content
    block footer

Кроме content, в этом макете используется footer:

extends page.pug

block content
  p Welcome

block footer
  p Thanks for visiting, come again soon!

Код приложения будет выглядеть так:

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 } })

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)
})

app.get('/courses', (req, res) => {
  const data = {
    courses: state.courses,
  }
  res.view('src/views/courses/index', data)
})

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

Структура файлов шаблонов с макетом выглядит так:

views
├── layouts
│   └── page.pug
├── courses
│   ├── edit.pug
│   ├── index.pug
│   ├── new.pug
│   └── show.pug
└── index.pug

Самостоятельная работа

  1. Сделайте макет сайта. Создайте в нем верхнее меню, которое будет содержать заголовок и ссылку на главную страницу сайта. Добавьте футер со ссылкой на ваш профиль в GitHub
  2. Реорганизуйте шаблоны в вашем приложении так, чтобы они использовали макет
  3. Убедитесь на практике в преимуществе макетов. Попробуйте вносить различные изменения в макет и проверьте, что изменения отражаются сразу на всех страницах приложения
  4. Отправьте изменения на GitHub

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

  1. Официальная документация Pug

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

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

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

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

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

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

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

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