Если проанализировать http
запросы к типичному сайту, то можно заметить, что большая часть этих запросов направлена на получение контента, а не его модификацию. Другими словами, основная работа обработчиков состоит в том, чтобы сформировать правильный html
и отправить его клиенту (браузеру). Единственный способ для генерации html
, с которым мы знакомы, это ручной сбор строчки, содержащей разметку, и отправка посредством метода send
.
app.get('/', (req, res) => {
res.send('<div>Hello World!</div>');
});
Сказать, что этот способ плох, это ничего не сказать. Кроме того, что это крайне неудобно, существует масса других недостатков в таком подходе. Если заглянуть в историю развития web, то выяснится интересный факт: php
появился как средство решения описанной выше задачи, а не как язык программирования.
Задачу по формированию разметки называют шаблонизацией, а конкретные библиотеки для шаблонизации называют шаблонизаторами. Общий принцип работы такой: описываются файлы с разметкой, а библиотека предоставляет функции для загрузки этих шаблонов в код. Во время загрузки происходят необходимые подстановки и шаблон заполняется конкретными данными.
Классическим примером может служить шаблонизатор jinja
из мира питона. Его популярность привела к тому, что в каждом языке есть множество шаблонизаторов, очень похожих и даже работающих так же, как jinja
. Поэтому можно говорить о целом классе jinja-like
шаблонизаторов.
<h1 class="header">{{ pagename | title }}</h1>
<div class="small">authors</div>
<ul>
{% for author in authors %}
<li{% if loop.first %} class="first"{% endif %}>
{{ author }}
</li>
{% endfor %}
</ul>
По сути jinja
— это хоть и примитивный, но полноценный язык программирования, который вкрапливается в файл с разметкой и расширяет его во время обработки. Несмотря на очевидность этого решения, оно обладает рядом недостатков. Первое — это сложность редактирования такого рода шаблонов. Из-за перемешивания кода с версткой приходится скакать вверх-вниз чтобы добавить/удалить/изменить теги, и то же самое нужно делать с конструкциями самого языка. Этот недостаток может быть не очевиден тем, кто никогда не видел альтернативных решений, и как мы увидим позже, они есть. Второе: в подобных шаблонизаторах текст вне конструкций шаблонизатора, то есть та самая вёрстка, никак не анализируется. Это легко приводит к проблемам типа "незакрытый тег", или семантическому нарушению html
, когда неправильно друг в друга вкладываются теги, используются несуществующие атрибуты и тому подобное. И третий немаловажный момент: оформление шаблонов не проверяется автоматическими инструментами, и поэтому стиль будет сильно зависеть от человека.
Существует и совершенно другой подход к организации шаблонов. Когда я в первый раз увидел такое, то был немало удивлен. Кажется, что самостоятельно дойти до этого решения очень сложно. Чтобы не томить, сразу покажу пример:
h1.header= pagename
.small authors
ul
each author, index in authors
li(class= index === 0 && "first")= author
Этот пример почти идентичен тому, что было выше с использованием jinja-like
шаблонизатора. Обратите внимание насколько чище шаблон во втором примере и на то, что он почти в два раза короче.
История таких шаблонизаторов берет свое начало с haml
, Ruby шаблонизатора, который в мире rails
является решением номер один уже очень много лет. После этого оно было скопировано во многие языки, как и jinja
. В js
мире haml-like
шаблонизатор был долгое время известен как jade
, и лишь недавно его переименовали в pug
.
Попробуем разобраться с основными принципами работы таких шаблонизаторов. Во-первых, это так же язык программирования, но в отличие от jinja-like
шаблонизаторов, то что не является кодом, на самом деле не является версткой. Всё, что пишется в pug
-шаблонах, будет обрабатываться парсером, другими словами, в haml-like
шаблонизаторах вы не можете писать всё что угодно вне управляющих конструкций. Во-вторых, шаблон строится с помощью особого синтаксиса, который задаёт теги в виде имен, а вложенность определяется отступом на следующем уровне.
И, с одной стороны, у вас появляется новый язык и новый способ построения html
. Что требует некоторого привыкания, но с другой, преимущества оказываются настолько сильными, что человек, распробовавший подобные шаблонизаторы, вряд ли добровольно вернется на jinja-like
библиотеки. Ниже перечислены основные преимущества:
Интеграция pug
с Express выглядит очень просто:
// npm install pug
app.set('view engine', 'pug');
app.get('/', (req, res) => {
const data = { title: 'Hey', message: 'Hello there!' };
res.render('index', data);
})
// index.pug
html
head
title= title
body
h1= message
Всё сводится к установке зависимости и установке pug
в качестве движка для рендеринга шаблонов. После этого, внутри обработчиков можно начинать использовать метод render
. Первый параметр которого - это путь до шаблона, второй - набор параметров для подстановок внутри шаблона.
Это не единственный способ передачи параметров в шаблон. В большинстве случаев они передаются именно вторым параметром в render
, но иногда возможны ситуации, в которых у нас есть сквозная функциональность, и было бы крайне неудобно прокидывать их в шаблон в каждом обработчике. Реализуется это через установку свойств в объект res.locals
, а в шаблоне эти свойства становятся доступны как переменные. Эту особенность мы будем использовать позже, когда начнем работать с сессиями и аутентификацией. Помните, что злоупотреблять этим способом не стоит, явное лучше неявного. Стремитесь к тому, чтобы код был чистый (использовал чистые функции).
На практике сайт не всегда состоит из уникальных страниц. Обычно меняется только контентная часть, а вокруг одно и тоже. Часть, которая не меняется, принято называть макетом или лейаутом (layout). Это настолько распространенный кейс, что большинство шаблонизаторов поддерживают механизм для выделения лейаутов. В pug
он называется наследованием шаблонов. Ниже приведён пример такого наследования.
//- layout.pug
html
head
title My Site - #{title}
block scripts
script(src='/jquery.js')
body
block content
block foot
#footer
p some footer content
//- page-a.pug
extends layout.pug
append scripts
script(src='/pets.js')
block content
h1= title
- const pets = ['cat', 'dog']
each petName in pets
h2= petName
В шаблоне, который мы используем для рендеринга нашей страницы, пишется специальная директива extends ...
. В неё передаётся имя окружающего шаблона, который чаще является макетом. В макете определяется блок (или блоки), в которые будет происходить подстановка кусков шаблона. Далее необходимо в шаблоне (не макете) определить такие же блоки и наполнить их контентом. Синтаксис задания блоков в обоих местах одинаковый, только в одном случае блок не содержит тела, а в другом содержит.
Так же бывает полезным механизм включения, позволяющий выделять из шаблонов общие части и переиспользовать их.
На просторах интернета постоянно спорят о том, что может быть в шаблоне, а чего нет. Что является логикой вывода, а что нет. При этом есть ряд правил, которые объективно нарушать не стоит:
ui
, блок показывается по определённому условию, то вы не сможете этого избежать, единственное о чем нужно помнить, это создавать вовремя правильные абстракции (функции) для избежания дублирования, а так же для выделения бизнес-правил.В отличие от js кода, Express автоматически перечитывает файлы с шаблонами после каждого запроса, другими словами вам не требуется помощь nodemon
для рестарта приложения при обновлении шаблонов.
Вам ответят команда поддержки Хекслета или другие студенты.
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.
Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно
Наши выпускники работают в компаниях:
Зарегистрируйтесь или войдите в свой аккаунт