Шаблонизатор Nunjucks от Mozilla

Поговорим о шаблонизаторе Nunjucks от Mozilla, который вдохновлен jinja2. Он достаточно мощный, поддерживает наследование шаблонов, асинхронное управление кодом, макросы, пользовательские теги и много другое. В нем есть все, что необходимо для комфортной фронтэнд разработки. В этой статье мы вкратце разберем основные возможности данного шаблонизатора.

Возможности шаблонизатора Nunjucks

Nunjucks vs Jinja2

Jinja2 появился раньше. Оба шаблонизатора очень схожи, но есть небольшие различия. Они написаны на разных языках. Nunjucks имеет доступ к нативным конструкциям JavaScript, а Jinja2 — к Python.

То есть, у вас могут возникнуть проблемы, например, значение true в Nunjucks (как и в JS) пишется с маленькой буквы, а в Jinja2 (как и в Python) с большой — True. Если не использовать нативные функции, а применять исключительно возможности самих шаблонизаторов, то шаблоны Nunjucks и Junja2 будут идентичны. Но все же есть некоторые моменты, которые реализованы в Jinja2, но еще не появились в Nunjucks, поэтому можно считать эти два шаблонизатора разными.

Nunjucks vs Pug

Ранее мы с вами разбирали основные возможности Pug. Если вы с ним знакомы, то сразу заметите, что они очень сильно отличаются друг от друга.

В Nunjucks вы по сути используете простой HTML, лишь точечно применяя возможности шаблонизации там, где нужно. А в Pug вы уже не сможете так сделать — все элементы верстки обязаны иметь строгий pug-синтаксис.

Давайте сравним. Код Pug:

.user
  .user__name
    = user.name
  .user__email
    = user.email
  a.user__site(href=user.site)
    | site
  if user.avatar
    img(src=user.avatar alt=user.name)

Код Nunjucks:

<div class="user">
  <div class="user__name">
    {{ user.name }}
  </div>
  <div class="user__email">
    {{ user.email }}
  </div>
  <a class="user__site" href="{{ user.site }}">
    site
  </div>
  {% if user.avatar %}
    <img src="{{ user.avatar }}" alt="{{ user.name }}">
  {% endif %}
</div>

Результат:

<div class="user">
  <div class="user__name">
    user
  </div>
  <div class="user__email">
    user@example.com
  </div>
  <a class="user__site" href="https://example.com">
    site
  </div>
  <img src="avatar.png" alt="user">
</div>

Расширение

В Nunjucks нет строгого требования к расширению файлов шаблонов, но все же принято использовать .njk.

Переменные

Переменные заключаются в двойные фигурные скобки, как во многих популярных шаблонизаторах:

{{ user }}

Если нужно обратиться к свойству, то используется точка или квадратные скобки, как в JavaScript:

{{ user.phone }}
{{ user["phone"] }}

Основные конструкции

Большинство конструкций в данном шаблонизаторе заключаются между {% и %}.

set

Отвечает за создание и модифицирование переменных. Они могут иметь любой тип: число, строка, объект и т.д. Мы также можем присваивать сразу нескольким переменным одинаковое значение.

Пример:

{% set username = "John" %}

{% set ingredients = {
  'кетчуп': '5 ст.л',
  'горчича': '1 ст.л',
  'соленый огурец': '1 шт.'
} %}

{% set x, y, z = 27 %}

if

Представляет из себя конструкцию условного вывода содержимого:

{% if hungry %}
  Я голоден
{% elif tired %}
  Я устал
{% else %}
  Я в порядке!
{% endif %}

Как и в JavaScript, вы можете использовать тернарное выражение, но в Nunjucks наличие else не обязательно:

{{ "true" if user else "false" }}
{{ "true" if user }}

for

По сути является обычном циклом for. Перебирает массивы и словари.

Пример перебора массива:

<ul>
{% for item in list %}
  <li>{{ item.title }}</li>
{% else %}
  <li>Будет отображаться это, если массив 'item' пуст</li>
{% endfor %}
</ul>

Перебор объекта:

{% set ingredients = {
  'кетчуп': '5 ст.л',
  'горчича': '1 ст.л',
  'соленый огурец': '0 шт.'
} %}

<ul>
{% for ingredient, amount in ingredients %}
  <li> Используйте {{ amount }} {{ ingredient }} </li>
{% endfor %}
</ul>

Результат:

<ul>
  <li> Используйте 5 ст.л кетчуп </li>
  <li> Используйте 1 ст.л горчича </li>
  <li> Используйте 0 шт. соленый огурец </li>
</ul>

asyncEach

Применяется только для асинхронных шаблонов. asyncEach ведет себя так же, как for, но позволяет асинхронно управлять циклом.

<ul>
{% asyncEach item in items %}
  {% include "item-template.html" %}
{% endeach %}
</ul>

asyncAll

Это то же самое, что и asyncEach. Также выводит все элементы асинхронно, но сохраняет их порядок.

Допустим, вам нужно вывести список пользователей из базы данных, это следует делать параллельно, т.к. последовательная загрузка может оказаться медленной. В примере ниже мы как будто выводим список id из базы данных:

<ul>
{% asyncAll user in users %}
  <li>{{ user.id }}</li>
{% endall %}
</ul>

macro

Макросы служат для переиспользования повторяющихся блоков. В Pug, например, это называется миксинами. Здесь смысл тот же — данная конструкция по сути является функцией, которая может принимать необходимые параметры и вызываться в нужном месте. Также могут использоваться параметры со значением по умолчанию.

В примере ниже мы видим макрос, который создает некий input:

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

Использование макроса:

{{ field('user') }}
{{ field('pass', type='password') }}

Результат:

<div class="field">
  <input type="text" name="user" value="" />
</div>

<div class="field">
  <input type="password" name="pass" value="" />
</div>

extends

В Nunjucks, как и в большинстве шаблонизаторов, поддерживается возможность наследования.

Чаще всего в проектах принято делать базовый шаблон base, в котором описывается основная структура страницы (такие вещи, как <!doctype>, <head>, подключение скриптов, стилей и т.д. — всё, что необходимо иметь на каждой странице разрабатываемого сайта). Затем уже другие шаблоны (дочерние) наследуются от базового (родительского) без дублирования структуры, но с возможностью дополнения.

Для указания шаблона, от которого мы будем наследоваться, необходимо использовать extends:

{% extends "base.html" %}

block

Используется для переопределения секций при наследовании шаблонов.

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

<head>
{% block meta %}
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width">
  <meta property="og:title" content="Заголовок">
{% endblock %}
</head>

В дочернем шаблоне мы можем переопределить этот блок:

{% extends "base.html" %}

{% block meta %}
  {{ super() }}
  <meta property="og:description" content="Описание">
{% endblock %}

И результатом будет следующее:

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width">
  <meta property="og:title" content="Заголовок">
  <meta property="og:description" content="Описание">
</head>

include

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

<div class="container">
  {% include "header.html" %}
  {% include "main-content.html" %}
  {% include "footer.html" %}
</div>

import

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

Например, мы имеем файл label.html, и его необходимо заимпортировать в другом файле:

{% import "label.html" as label %}

Теперь можно получить доступ к переменным и функциям файла label.html, обращаясь к label.

Экранирование

Если у вас настроено автоматические экранирование, то все выходные значения будут экранированы, то есть безопасны. А если требуется вручную явно отметить переменную как безопасное выражение, используется фильтр safe.

Пример:

{{ tagSpan }}
{{ tagSpan | safe }}

Результат:

&lt;span%gt;
<span>

Регулярные выражения

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

{% set regExp = r/^foo.*/g %}

{% if regExp.test('foo') %}
  Foo нашелся!
{% endif %}

Комментирование

Комментарии записываются очень просто:

{# Так записываются комментарии в Nunjucks #}

Выражения

Математические операторы

Доступны следующие математические операторы:

  • Сложение +
  • Вычитание -
  • Деление /
  • Умножение *
  • Целочисленное деление //
  • Остаток от деления %
  • Умножение *
  • Возведение в степень **

Пример:

{{ 2 + 1 }}
{{ 10 / 5 }}
{{ 2 ** 6 }}

Операторы сравнения

  • Равно ==
  • Строгое равно ===
  • Не равно !=
  • Строгое не равно !==
  • Больше >
  • Больше или равно >=
  • Меньше <
  • Меньше или равно <=

Логические операторы:

  • И — and
  • Или — or
  • Не — not

Основные встроенные фильтры

abs

Возвращает абсолютное значение аргумента:

{{ -3 | abs }}

Результат:

3

capitalize

Делает первую букву в строке заглавной, оставшиеся — строчными:

{{ "Используйте Шаблонизаторы Для Удобства" | capitalize }}

Результат:

Используйте шаблонизаторы для удобства

upper

Приводит строку к верхнему регистру:

{{ "вывести всё большими буквами" | upper }}

Результат:

ВЫВЕСТИ ВСЁ БОЛЬШИМИ БУКВАМИ

Есть противоположный ему фильтр lower, который приводит строку к нижнему регистру.

escape

Экранирует выводимое значение:

{{ "<html>" | escape }}

Результат:

&lt;html&gt;

indent

Задает табуляцию с помощью пробелов. По умолчанию их число равно 4, но вы можете спокойно поменять, передав параметр с необходимым количеством.

{{ "list\item\item" | indent }}

Результат (4 пробела):

list
    item
    item

Пример с параметром:

{{ "list\item\item" | indent(2) }}

Результат (2 пробела):

list
  item
  item

trim

Убирает все пробелы до и после:

{{ " содержимое " | trim }}

Результат:

содержимое

join

Возвращает строку, которая является объединением строк:

{% set items = [1, 2, 3] %}

{{ items | join }}

Результат:

123

round

Отвечает за округление чисел:

{{ 4.5 | round }}

Результат:

5

Можно округлить число до ближайшего целого в меньшую строну:

{{ 4.5 | round(0, "floor") }}

Результат:

4

А также можно передать только один параметр — это будет означать количество цифр после запятой:

{{ 4.12346 | round(4) }}

Результат:

4.1235

string

Преобразует объект в строку:

{% set item = 123456789 %}

{% for i in item | string | list %}
  {{ i }},
{% endfor %}

Результат:

1,2,3,4,5,6,7,8,9

Nunjucks предоставляет множество возможностей, подобных JavaScript: циклы, разнообразные математические функции, регулярные выражения, фильтры, но этим не стоит злоупотреблять — основная логика должна находиться в скриптах, а не шаблонах.