При разработке сайта количество одинаковых по структуре блоков велико. Представьте каталог интернет-магазина. Он может состоять из сотен карточек, которые отличаются только данными. При разработке «по старинке», без шаблонизаторов, все эти карточки верстаются методом «копировать-вставить». Не идеальное решение, но вполне рабочее. Именно так кажется до тех пор, пока не произойдет изменение карточек. Любое добавление/удаление элементов приведет к часам, а то и дням работы по изменению верстки.
Это кажется невероятным, но похожие ситуации встречаются не настолько редко, как может показаться. Поэтому разработчику важно научиться работать с повторяющимися участками кода.
Для переиспользования верстки в Pug используются миксины — конструкции, позволяющие описывать и подставлять код в любую часть верстки. Это отличное решение для создания компонентов.
В качестве примера используем компонент кнопки. Это несложная конструкция, которая может состоять всего из одного тега. Для создания миксина используется ключевое слово mixin
, после чего идет произвольное имя миксина. Это имя вы выбираете сами, при этом не забывайте о грамотном именовании. Внутрь миксина помещается весь HTML, который будет нужен. В данном случае один тег <button>
:
mixin buttonOrder
button.btn.btn-order Оформить заявку
Вот и все. Простая конструкция, но с множеством возможностей, о которых поговорим чуть ниже. Сейчас необходимо вызвать этот миксин, чтобы его тело попало в итоговый HTML-код. Здесь важно понимать, что сам миксин никак не попадает в итоговую разметку. Для вызова миксина указывается символ +
и название миксина. После этого он подставится в HTML-код:
mixin buttonOrder
button.btn.btn-order Оформить заявку
+buttonOrder
<button class="btn btn-order">Оформить заявку</button>
Каждый миксин может быть вызван неограниченное количество раз:
mixin buttonOrder
button.btn.btn-order Оформить заявку
+buttonOrder
+buttonOrder
+buttonOrder
<button class="btn btn-order">Оформить заявку</button>
<button class="btn btn-order">Оформить заявку</button>
<button class="btn btn-order">Оформить заявку</button>
Если миксины просто добавляют уже существующую разметку, то не логичнее ли вынести ее в отдельный файл и подключать с помощью include
? Споры об этом возникают постоянно, но стоит держать в голове семантику этих конструкций: миксины нужны для выделения определенного куска верстки в обертку для ее быстрого переиспользования. Подключение файла необходимо для добавления больших логических конструкций: шаблон, скрипты, объекты или массивы.
Если нужна обертка для логического компонента, то используется mixin
, если нужно подключить шаблон в конкретный Pug-файл, то используется конструкция include
.
Миксины как функции
В Pug миксины выполняют роль не только подстановщиков HTML-кода, но и функций, которые возвращают HTML. Отличительная черта таких миксинов — наличие аргументов, которые используются при вызове миксина. Вернемся к системе кнопок из прошлого примера. На страницах приложения часто бывает несколько различных видов кнопок. Они могут различаться текстом, классами и так далее.
Можно создать необходимое количество миксинов, но, если элементы подчиняются единой дизайн-системе, то это не самый удобный вариант, так как при изменении общего стиля кнопок возникнет необходимость править каждый миксин.
Добавим возможность передачи произвольного текста в миксин с кнопкой. Для этого, при определении миксина указываются нужные аргументы в скобках. Имя аргумента всегда произвольное и зависит от вас. Внутри миксина такой аргумент будет интерпретироваться как обычная переменная, поэтому и способ работы с ней стандартный:
mixin button(title)
button.btn.btn-order= title
Вызов таких миксинов осуществляется следующим образом:
mixin button(title)
button.btn.btn-order= title
+button('Заказать')
<button class="btn btn-order">Заказать</button>
Миксины могут принимать и значения по умолчанию. Это полезно в случае, когда большинство элементов имеют одинаковый контент. Достаточно присвоить нужное значение в определении миксина:
mixin button(title = 'Заказать')
button.btn.btn-order= title
+button
+button('Оформить заявку')
<button class="btn btn-order">Заказать</button>
<button class="btn btn-order">Оформить заявку</button>
Если используется дефолтное значение аргумента, то нет необходимости добавлять пустые круглые скобки при вызове, как это происходит в Sass.
Миксины также удобно использовать в качестве шаблонизации, комбинируя изученные ранее возможности. Например, вывод пользователей в таблице. Если на сайте есть несколько различных таблиц пользователей, то удобнее всего создать нужный миксин и передавать в него необходимые данные о пользователях:
-
const users = [
{
name: 'Кодовенок',
surname: 'Хекслетович',
login: 'hexlet-code',
score: 1271
},
{
name: 'Король',
surname: 'Верстки',
login: 'king-of-layout',
score: 1100
},
]
mixin createUserTable(usersData = [])
table
thead
tr
th Имя
th Фамилия
th Логин
th Баллы
tbody
each user in usersData
tr
td= user.name
td= user.surname
td= user.login
td= user.score
else
tr
td(colspan='4') Пользователи отсутствуют
section
h2 Лучшие из лучших
+createUserTable(users)
h2 Худшие из худших
+createUserTable(badUsers)
Единожды создав миксин, у вас появилась возможность использовать его с абсолютно разными данными, которые придут в Pug. Можно пойти еще дальше и проверять поля, типы значений, валидировать и нормализовать данные. В качестве значения по умолчанию был указан пустой массив. Это гарантирует, что если данные отсутствуют или переменная не существует, то будет подставлен пустой массив:
<section>
<h2>Лучшие из лучших</h2>
<table>
<thead>
<tr>
<th>Имя</th>
<th>Фамилия</th>
<th>Логин</th>
<th>Баллы</th>
</tr>
</thead>
<tbody>
<tr>
<td>Кодовенок</td>
<td>Хекслетович</td>
<td>hexlet-code</td>
<td>1271</td>
</tr>
<tr>
<td>Король</td>
<td>Верстки</td>
<td>king-of-layout</td>
<td>1100</td>
</tr>
</tbody>
</table>
<h2>Худшие из худших</h2>
<table>
<thead>
<tr>
<th>Имя</th>
<th>Фамилия</th>
<th>Логин</th>
<th>Баллы</th>
</tr>
</thead>
<tbody>
<tr>
<td colspan="4">Пользователи отсутствуют</td>
</tr>
</tbody>
</table>
</section>
Передача вложенного контента в миксин
Миксины могут использоваться в виде «скелета» структуры компонента. Это может быть оформление статьи, которая выводится на странице в виде следующего шаблона:
<article class="post">
<h2>Название статьи</h2>
<div class="post-body">
<p>Текст статьи</p>
</div>
<div class="post-author">
<p>Автор статьи</p>
</div>
</article>
Если таких статей много на сайте, то есть смысл вынести шаблон в отдельный миксин. Но как же передать данные? Тут есть два пути:
- Определить аргументы, и передавать их отдельно в миксин.
- Каким-то образом сразу передать всю статью.
Первый способ может быть неудобен из-за тела статьи. Ведь статья не является произведением из одного предложения, которое можно передать в виде аргумента миксина. К тому же статьи зачастую хранятся в отдельном файле, как было в уроке про include
. Поэтому лучшим вариантом в данной ситуации может считаться передача текста непосредственно в миксин с использованием вложенности. Вызовем миксин article
, передав в качестве аргументов название и автора статьи, а в тело поместив подключение файла:
+article('Название статьи', 'Автор статьи')
include:markdown-it main.md
Главный вопрос: а куда подставится результат выполнения include:markdown-it main.md
. На самом деле никуда! Миксин не знает, что делать с этими данными, хотя и может их принять. Для подстановки вложенных в миксин данных используется конструкция block
, в которой и хранится то, что было вложено в миксин. Можно считать это «невидимым аргументом», который всегда есть, но не объявляется явно. Теперь можно реализовать логику миксина:
mixin article(name, author)
.post
h2= name
.post-body
block
.post-author= author
+article('Название статьи', 'Автор статьи')
include:markdown-it main.md
<div class="post">
<h2>Название статьи</h2>
<div class="post-body">
<p>Текст статьи из файла main.md</p>
</div>
<div class="post-author">Автор статьи</div>
</div>
Дополнительное задание
Доработайте миксин с добавлением статьи на страницу:
- Проверьте, что существует контент для отображения. Если его нет, то выведите ошибку
- Добавьте значения по умолчанию для аргументов. В качестве имени статьи используйте «Hexlet». В качестве автора «Кодовенок»
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.