Nunjucks: Шаблонизатор для JavaScript

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

  • для совместного использования разметки на страницах;
  • для включения отдельных блоков с функциональностью (при необходимости);
  • в общем и целом для упрощения процесса построения в меру сложных макетов страниц c минимумом прилагаемых усилий.

Сравнение шаблонизаторов

Существует ряд популярных шаблонизаторов для JavaScript, и мы рассмотрим некоторые из них:

EJS

EJS (Встроенный JavaScript) очень напоминает PHP или JSP, очень простые функции – слишком простые для того, чтобы быть хоть в чем-то полезными. Сразу исключаем.

Jade

Наиболее характерной особенностью Jade является то, что он обозначает пробел без кавычек (как Coffeescript),что или является чудом (если вы верите в подобные вещи),  или катастрофической неисправностью, если вы реалист (как я). По своим характеристикам этот шаблонизатор стоит на одном уровне с большинством других систем, но его стилевое оформление заставляет нас почти сразу его отбросить.

Mustache / Handlebars

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

Dust

В настоящее время популярен из-за его применения в LinkedIn. На первый взгляд, очень похож на Mustache / Handlebars, но дополнен  рядом полезных  функций, таких как именованные блоки. Однако процесс загрузки шаблона и рендеринг выглядят довольно неуклюже, и он до сих пор не поддерживает довольно широкий спектр функций для построения страницы. Хорош, но недостаточно…

Nunjucks

Еще одна {{ система на основе токенов }}, обеспечивающая (как и другие движки) логику, выполнение циклов и возможность управления переменными.   Но кроме этого, шаблонизатор предоставляет  продвинутые элементы построения страницы, например,  наследование блоков, включая наследование макетов, пользовательские теги и макросы. Это все наилучшим образом подходит для системы CMS, где страницы представляют собой серию «строительных блоков».

Итак, у нас есть победитель!

Для справки

Некоторые полезные ссылки для сравнения шаблонизаторов:

Основные примеры рендеринга

Давайте рассмотрим некоторые примеры шаблонизатора Nunjucks в действии…

Динамический рендеринг на платформе Node при использовании nunjucks.render

Во-первых, мы рассмотрим пример, в котором будем использовать  Nunjucks для рендеринга страницы в ответ на запрос веб-службы,  который обрабатывается сервером Node. Этот сценарий очень похож на тот, который  происходит при запросе  PHP или СoldАusion или страницы ASP .NET на обычном веб-сайте или в веб-приложении.

Предположим, что Node уже установлен.

Устанавливаем Express и Nunjucks:

npm install express --save
npm install nunjucks --save

Создаем нашу базовую структуру app.js:

var express = require('express') ;
var nunjucks = require('nunjucks') ;
var app = express();

Настраиваем Nunjucks:

var PATH_TO_TEMPLATES = '.' ;
nunjucks.configure(PATH_TO_TEMPLATES, {
   autoescape: true,
   express: app
});

Простой путь:

app.get('/home.html', function(req, res) {
   return res.render('index.html') ;
});
app.listen(3000);

Создаем шаблон index.html:

<p>Привет всем</p>

Запуск с:

node app.js

и переходим на  http://localhost:3000. Готово!

Добавление динамических данных

Давайте быстро расширим этот пример, чтобы показать, как можно  передать данные вашему шаблону в Nunjucks.

Для начала давайте передадим некоторые данные функции  render :

app.get('/home.html', function(req, res) {
   var data = {
       имя: 'Энди',
       фамилия: 'Нил'
   } ;
   return res.render('index.html', data) ;
});

Во-вторых, мы будем ссылаться на эти данные в нашем шаблоне index.html :

<p>Привет {{ data.имя }}</p>

Угадали, что произойдет, когда мы перейдем на http://localhost:3000?

Предварительная компиляция при помощи Gulp

Если у вас есть шаблоны, которые не опираются на динамические данные, тогда альтернативой их рендерингу по запросу будет использование инструмента Gulp (или Grunt, если он вам больше по душе) для предварительной компиляции данных во время компоновки.

Предположим, у вас есть простой сайт, состоящий из двух страниц со схожей разметкой:

  • src/index.html
  • src/contact-us.html
  • src/_layout.html

Ваши странички могут выглядеть так:

index.html

{% set title = 'Главная' %}
{% extends '_layout.html' %}
{% block content %}
<h1>Добро пожаловать</h1>
<p>Разве это не самый лучший сайт в мире?</p>
{% endblock %}

contact-us.html

{% set title = 'Контакты' %}
{% extends '_layout.html' %}
{% block content %}
<h1>Контакты</h1>
<p>Звоните нам на 0800 000 0000 или отправьте письмо на эл. адрес <a href="mailto:[email protected]?subject=Nothing">[email protected]</a>.</p>
{% endblock %}

_layout.html

<!DOCTYPE>
<html>
<head>
   <title>{{ title }}</title>
</head>
<body>
{% block content %}
{% endblock %}
</body>
</html>

Устанавливаем плагин gulp-nunjucks:

npm install gulp-nunjucks --save

Создаем простой gulpfile.js в корневой папке проекта:

var gulp = require('gulp') ;
var nunjucks = require('nunjucks') ;
var COMPILE = {
   SRC: '/src/**.html',
   DEST: '/dist’
};
gulp.task('render', function() {
   return gulp.src(COMPILE.SRC)
   .pipe(nunjucks())
   .pipe(gulp.dest( COMPILE.DEST )) ;
});

Запускаем процесс рендеринга:

gulp render

Полученные в результате страницы HTML будут выглядеть так:

index.html

<!DOCTYPE>
<html>
<head>
   <title>Главная</title>
</head>
<body>
<h1>Добро пожаловать</h1>
<p>Разве это не самый лучший сайт в мире?</p>
</body>
</html>

contact-us.html

<!DOCTYPE>
<html>
<head>
   <title>Контакты</title>
</head>
<body>
<h1>Контакты</h1>
<p>Позвоните нам на 0800 000 0000 или отправьте письмо на эл. адрес <a href="mailto:[email protected]?subject=Nothing">[email protected]</a>.</p>
</body>
</html>

Особенности Nunjucks

Полную документацию можно посмотреть здесь:

https://mozilla.github.io/nunj…

В этом разделе мы затронем некоторые из основ для иллюстрации некоторых ключевых функций Nunjucks.

Наследование шаблона

Наследование шаблона — это самый простой путь к многократному использованию шаблонов.  При написании шаблона можно определить «блоки», которые дочерние шаблоны могут переопределить. (Это то, что мы делали выше в разделе {% содержание блока %}).

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

Включения

Обратная сторона расширений – это то, что они включают шаблон внутрь текущего шаблона. Это полезно для «вытягивания» блоков  контента, которые используются в нескольких местах на сайте или в приложении.

Импортирование и макросы

Импортирование позволяет загружать шаблон и открывать любые переменные и макросы (функции), определенные в нем. Это может быть полезно для таких вещей, как создание функций для последовательного отображения полей формы… немного похоже на написание пользовательских тегов с помощью ColdFusion, например. Для наглядности давайте  рассмотрим  шаблон под названием forms.html, который выглядит так:

{% macro field(name, value = '', type = 'text') %}
<div class="field">
<input type="{{ type }}" name="{{ name }}" value="{{ value | escape }}" />
</div>
{% endmacro %}
{% macro label(text) %}
<div>
<label>{{ text }}</label>
</div>
{% endmacro %}

Мы можем импортировать этот шаблон и использовать его для быстрого рендеринга серии полей формы:

{% import "forms.html" as forms %}
{{ forms.label('Username') }}
{{ forms.field('user') }}
{{ forms.label('Password') }}
{{ forms.field('pass', type = 'password') }}

Это особенно полезно при использовании таких фреймфорков, как  Foundation или Bootstrap, которые требуют разумного количества HTML и CSS сред — если у вас это получилось путем рендеринга в макросе, то код вашего шаблона будет гораздо проще. (Плюс, если ваш шаблон  делает весь рендеринг  через макросы, то он напрямую не привязан к фреймворку, что облегчает переключение фреймфорков, а также позволяет делать изменения, общие для всех полей формы определенного типа).

Логика – If / For / While

if проверяет состояние и используется для выборочного отображения контента или выполнения других операций :

{% if variable %}
Переменная есть, ура
{% endif %}
{% if user.authorised %}
{% extends "logged-in.html" %}
{% else %}
{% extends "logged-out.html" %}
{% endif %}

for зацикливает массивы:

код

var messages = ['Необходимо ввести адрес эл. почты', 'Пароль должен быть не короче 8 знаков'];

шаблон

<div class="wrapper">
   <div id="message_area">
       <div class="messages status">
       {% for message in messages %}
       {{ message }}<br />
       {% endfor %}
       </div>
   </div>
</div>

Следующие шаги

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