Pug.js – мое знакомство с мопсовым препроцессором
Привет. Недавно решил собрать себе архив из своих работ по вёб-дезигну, которые делал последние несколько лет, до фронт-энда. Оказалось их немало, большая часть из них, к сожалению, утрачены, но даже так получился список из двадцати с чем-то работ. А раз уж я теперь фронт-энд разработчик, то запилю это всё дело в виде странички.
Так как это, своего рода, пет-проект, параллельно захотелось освоить что-то новенькое и как-то сам по себе выбрался препроцессор для вёрстки Pug. Если честно, я из-за названия и выбрал. 😃
У Pug'а интересный python'оподбоный синтаксис, основанный на индентах, то бишь вложенность элементов определяется не закрывающими тегами, а отступом:
ul.list
li.list__item Item A
ul.list__sublist
li.list__subitem#unique-id Subitem A1
li.list__subitem Subitem A2
li.list__item Item B
li.list__item Item C<ul class="list">
<li class="list__item">Item A
<ul class="list__sublist">
<li class="list__subitem" id="unique-id">Subitem A1</li>
<li class="list__subitem">Subitem A2</li>
</ul>
</li>
<li class="list__item">li Item B</li>
<li class="list__item">li Item C</li>
</ul> Класс можно задать через точку, ID через диез, прям как селектор в CSS, правда на ID-шник через диез ругается линтер:
Лучше его задавать как обычные атрибуты, которые задаются в скобках через запятую, как своеобразные аргументы функции:
img(src='./pic.png', alt='super pic', width='100', height='50')
Но одним относительно упрощенным синтаксисом, конечно, Pug не ограничивается. Так как это препроцессор, в нём возможны все "программистские" штучки - условия, циклы, интерполяция и функции. Можно еще прямо в .pug файле писать JavaScript, ограничено, но можно.
Правда, ни условиями ни циклами я так и не воспользовался, но воспользовался тремя инструментами, которые значительно сэкономили мне время.
Mixins
Это такая функция-конструктор, мы задаём ей параметры в аргументах, внутри задаём шаблон и эти параметры расставляем где надо, затем вызываем:
mixin item(foo, baz, bar)
li
h3 foo
img(src=baz, alt=foo)
p bar
+item('Mr. Puggy', './mr-puggy.png', 'Distinguished gentleman')Я использовал его для карточек в списке своих работ:
У меня тут наворочено с датой и с ссылкой на изображение. Дату я задаю в специальном, валидном для атрибута datetime и JS формате, но для отображения на странице преобразую в строки с годом и месяцем на английском, а потом подставляю интерполяцией в контент.
С ссылкой на изображения посложнее так как одно изображение, с целью оптимизации загрузки, существует аж в шести вариантах – jpg и jpg@2x для ретины, webp и webp@2x, и еще avif и avif@2x. Я сделал через конкатенацию строк, так как через интерполяцию в srcset, вроде как, не получится, по крайней мере линтер на меня ругался и упаковщик послал меня на 404.
Includes
По своей сути это аналог require() из PHP - позволяет импортировать кусок внешнего кода в файл. И также позволяет использовать компонентный подход непосредственно в вёрстке.
Например, у себя я использовал это в хэдере - это блок состоящий из двух блоков - лого и менюшка, которые я подключаю к нему через include:
При этом, все блоки лежат в своих директориях со своими стилями и скриптами:
Возможно, это немножечко перебор, но я решил попробовать и никто меня не остановит. Му-ха-хах!
Template Inheritance
Google переводит как "наследование шаблонов" - к файлу можно подключить структуру внешнего файла. То есть не вставить как кусок разметки через include, а прям импортировать всё и имея ввиду этот импорт уже работать.
Такой шаблон должен иметь в себе блоки block - участки шаблона контент которых можно изменять уже в файле куда подключаем шаблон.
html
head
title Mr. Puggy Personal Page
block style
link(rel='stylesheet', href='./style.css')
body
...Импортируем шаблон в рабочий файл через команду extends:
extends template.pug
А с блоками можно работать тремя способами – можно перезаписать контент блока просто вызвав его:
block style link(rel='stylesheet', href='./super-style.css')
При этом запись из шаблона удалится, то есть стили style.css не загрузятся. Еще можно добавить записи к шаблону с помощью append - в конец и prepend - в начало блока:
prepend style link(rel='stylesheet', href='./normalize.css') append style link(rel='stylesheet', href='./super-style.css')
Что на выходе, в HTML, даст нам все три тега в нужном нам порядке:
<head> <title>Mr. Puggy Personal Page</title> <link rel="stylesheet" href="normalize.css"> <link rel="stylesheet" href="style.css"> <link rel="stylesheet" href="super-style.css"> </head>
Самый простой пример, который я и использовал - я создал шаблон _layout.pug для типичной страницы, где есть обязательные теги типа doctype, html, head ну и body. Писать их сто раз на каждой странице дело не боярское, поэтому мы и выводим в шаблон повторяющиеся элементы, а те места которые будут потенциально изменяться оборачиваем в конструкцию block.
Например, в head явно есть мета-информация которая нужна везде:
head meta(charset='utf-8') meta(content='width=device-width, initial-scale=1', name='viewport') meta(content='IE=Edge', http-equiv='X-UA-Compatible')
А теги типа title, какие-то дополнительные стили и скрипты, аля пиксели соц. сетей или метрик, не говоря уже о всяких SEO-тегах - нам нужно подставлять индивидуально для страницы.
Грубо говоря, я сделал нечто подобное:
Импортирую этот _layout.pug на страницу, а в head подставляю всё что нужно через append. Таким образом, у меня и мета и фавиконки и общие стили уже есть, остаётся добавить несколько строк.
В body схема похожая, правда я до конца в ней не уверен:
Тут комбинация блоков и инклудов. Так как блоки можно переписать, можно в любой момент поменять любой блок с условного плейсхолдера на то что нужно.
А, собственно все, спасибо за внимание. Как допилю всё - закину сюда ссылочку или может еще чего напишу. Там я еще со сборщиком Parcel'ем развлекаюсь, есть о чем по бухтеть.