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'ем развлекаюсь, есть о чем по бухтеть.