Шаблонизация
1. Введение
Шаблонизация (templating) — метод связывания данных и разметки. Удобный способ генерации HTML по шаблону и данным. Используется на клиенте и сервере.
Суть шаблонизации заключается в том, чтобы отделить описание HTML от логики. Разметка помещается в отдельные файлы (шаблоны), а в местах, где необходимо вывести данные, размещаются специальные псевдопеременные. JS-код загружает нужный шаблон и заменяет в нем псевдопеременные на соответствующие данные.
На обычных сайтах, где HTML и CSS уже есть, элементы интерфейса получают готовый DOM и используя JavaScript вешают обработчики, оживляя его. Но в сложных интерфейсах разметка изначально отсутствует на странице. DOM создается с помощью JavaScript-кода динамически на основе данных, полученных с сервера или из других источников.
Представьте список постов на веб-странице, а так же любые другие ситуации, которые требуют отображения коллекции однотипных элементов, но с разными данными.
Данные для постов приходят от сервера как массив объектов, у нас есть шаблон одного поста. Используя цикл, мы можем пройтись по массиву объектов и вызвать функцию-шаблон для каждого объекта, результатом будет строка с подставленными данными. После чего мы в контейнер для постов просто повесим строку и браузер создаст разметку.
Это - стандартный подход написания динамических элементов интерфейса, данные для которых изменяются со временем.
1.1. Использование шаблонизации
Весь процесс требует нескольких простых шагов:
Наличие данных, которыми будет наполнятся элемент интерфейса
Шаблон, по которому будет составлена разметка элемента
Библиотека, которая предоставляет средства шаблонизации

Мы подробно рассмотрим это на примерах, но по сути это очень просто:
Подключить в проект выбранную библиотеку для шаблонизации
Составить HTML-шаблон необходимого вида
Сделать выборку шаблона в JS-файле
Произвести рендеринг шаблона вместе с данными
2. Шаблон
Шаблон — это строка в специальном формате, которая путём подстановки значений и выполнения встроенных фрагментов кода превращается в HTML.
Синтаксис шаблона зависит от библиотеки, самое важное - это понимать принцип работы шаблонизаторов. Мы будем использовать библиотеку Handlebars.
2.1. Синтаксис
Шаблон - это строка со специальными разделителями, которых в Handlebars всего три:
<!--
Выражение expr между разделителями будет выполнено как есть,
то есть вместо имени переменной будет подставлено ее значение,
при этом если переменная содержит строку с тегами, то не произойдет
парса строки.
-->
<div>{{expr}}</div>
<!--
Используется для вставки HTML-тегов, то есть выражение будет
распарсеной браузером на наличие тегов.
-->
<div>{{{expr}}</div>
<!--
Используется для встроенных и кастомных операций, к примеру #each
работает как forEach, перебирая коллекцию, а в теле each к
элементу коллекции можно обратиться как this. А #if можно использовать
для ветвлений.
-->
<div>{{#helper}}{{/helper}}</div>
Copy
То есть синтаксис Handlebars
- это обычный HTML, с вставками вида {{...}}
. Именно в те места, где указаны вставки, будут помещены данные.
<div class="menu">
<h3 class="menu-title">{{title}}</h3>
<ul class="menu-list">
{{#each items}}
<li class="menu-item">{{this}}</li>
{{/each}}
</ul>
</div>
Copy
2.2. Методы хранения
Шаблон - это просто многострочный HTML-текст, ему не место в файле скриптов. Один из способов — записать его в HTML-файле в тег template
.
Более современный способ, например при использовании Webpack
, хранить шаблон в отдельном файле и импортировать его. Для этого в конфигурацию Webpack
нужно добавить загрузчик Handlebars-шаблонов.
Внешние шаблоны имеют много преимуществ, главным образом в том, что шаблоны никогда не будут загружаться клиенту, если они не нужны на странице. Для усвоения ключевых концепций будем пользоваться встроенными шаблонами.
Давайте обернем шаблон в тег template
и дадим ему уникальный идентификатор, чтобы можно было выбрать в JS-файле по селектору.
<template id="menu-template">
<div class="menu">
<h3 class="menu-title">{{title}}</h3>
<ul class="menu-list">
{{#each items}}
<li class="menu-item">{{this}}</li>
{{/each}}
</ul>
</div>
</template>
Copy
3. Использование шаблона
Вернемся к перечисленным шагам, где мы описали последовательность действий, необходимых для использования шаблонизации, и разберем по пунктам.
3.1. Добавить в проект библиотеку
Подключение библиотеки в проект происходит очень просто. Есть несколько вариантов.
Скачать файл библиотеки и подключить его в
index.html
Использовать CDN-сервис для получения ссылки на файл библиотеки
Если используется инструмент вроде
Webpack
, можно ставить библиотеку как npm-пакет
Пока что используем CDN. В разделе документации о установке библиотеки есть ссылка на CDN-сервис на котором можно скопировать необходимый URL. Это - минифицированая версия библиотеки. Все что нужно сделать — это добавить еще один тег script
перед нашим файлом скриптов и перед закрывающим тегом body
в index.html
.
<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.11/handlebars.min.js"></script>
Copy
3.2. Составить HTML шаблон
Этот шаг мы выполнили выше и у нас уже есть полностью готовый шаблон для меню. Добавим его перед всеми скриптами в документе.
<body>
<!-- html разметка -->
<template id="menu-template">
<div class="menu">
<h3 class="menu-title">{{title}}</h3>
<ul class="menu-list">
{{#each items}}
<li class="menu-item">{{this}}</li>
{{/each}}
</ul>
</div>
</template>
<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.11/handlebars.min.js"></script>
<script src="js/scripts.js"></script>
</body>
Copy
3.3. Сделать выборку шаблона в скрипте
template
- это тег, значит мы можем выбрать его по селектору тега/класса/идентификатора. Хорошей практикой считается давать тегам template
, содержащим шаблон, идентификаторы, так как они уникальны.
Теперь в JS-файле мы можем по id
выбрать сам тег template
и его контент в виде строки, для этого используем свойство innerHTML
.
const source = document.querySelector('#menu-template').innerHTML.trim();
Copy
3.4. Отрендерить шаблон с данными
У нас уже есть библиотека и шаблон из которого мы изъяли текстовый контент. Для работы с шаблоном в библиотеке Handlebars
есть функция compile
. Эта функция запускает компиляцию шаблона source
и возвращает результат в виде функции, которую далее можно запустить с данными и получить строку-результат.
Handlebars.compile(source)
Copy
Вызов Handlebars.compile(source)
разбивает HTML-строку по разделителям и при помощи new Function
создаёт на её основе функцию. Тело этой функции создаётся таким образом, что код, который в шаблоне оформлен как {{...}}
, попадает в неё как есть, а переменные и текст прибавляются к специальному временному буферу, который в итоге возвращается.
const source = document.querySelector('#menu-template').innerHTML.trim();
const template = Handlebars.compile(source);
Copy

Теперь используя функцию-шаблон template
можем передать ей данные как аргумент и она вернет HTML-строку.
Для начала добавим данные для списка. В шаблоне указаны какие-то переменные title
и items
.
В реальных задачах сначала создаются форматы для данных, а потом под них пишутся шаблоны, но для наглядности мы для шаблона напишем данные.
Данные для шаблона - это что угодно, строка, объект, массив и т.д., зависит от задачи, чаще всего - объект или массив объектов. Так как у нас список с заголовком и набором пунктов, нам удобно использовать объект такого вида.
const menuData = {
title: 'Eat it createElement, templates rule!',
items: ['Handlebars', 'LoDash', 'Pug', 'EJS', 'lit-html'],
};
Copy
Следующим шагом будет вызвать функцию-шаблон и передать ей menuData
как аргумент. В результате получим строку с подставленными значениями, поместим ее в тег и браузер, распарсив ее, создаст HTML-разметку.
4. Шаблоны и Webpack
Когда используем сборщик модулей, очень удобно работать со внешними шаблонами.
Добавляем библиотеку.
npm install handlebars
Copy
Добавляем загрузчик handlebars-loader.
npm install --save-dev handlebars-loader
Copy
Обновляем конфигурацию Webpack, добавляя настройки загрузчика.
// В webpack.config.js
{
...
module: {
rules: [
...
{ test: /\.hbs$/, exclude: /node_modules/, use: "handlebars-loader" }
]
}
}
Copy
В папке src
создаем папку templates
, в которой добавляем файлы шаблонов с расширением .hbs
. Для нашего меню это будет menu.hbs
. Помещаем разметку шаблона в файл, без тега template
.
<!-- В menu.hbs -->
<div class="menu">
<h3 class="menu-title">{{title}}</h3>
<ul class="menu-list">
{{#each items}}
<li class="menu-item">{{this}}</li>
{{/each}}
</ul>
</div>
Copy
Там, где хотим использовать шаблон, импортируем файл с шаблоном. Особенность в том, что при импорте, handlebars-loader
обработает файл шаблона и в menuTemplate
уже будет лежать скомпилированная функция-шаблон готовая к использованию.
// В app.js
import menuTemplate from '/path/to/templates/menu.hbs';
const menuData = {
title: 'Eat it createElement, templates rule!',
items: ['Handlebars', 'LoDash', 'Pug', 'EJS', 'lit-html'],
};
const markup = menuTemplate(menuData); // html разметка с подставленным значениями
Copy
5. Дополнительные материалы
Last updated