Веб-разработка
May 18, 2023

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:

При этом, все блоки лежат в своих директориях со своими стилями и скриптами:

Конкретно тут скриптов нет :D

Возможно, это немножечко перебор, но я решил попробовать и никто меня не остановит. Му-ха-хах!

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 схема похожая, правда я до конца в ней не уверен:

Нужно еще по хорошему поменять .modal на dialog

Тут комбинация блоков и инклудов. Так как блоки можно переписать, можно в любой момент поменять любой блок с условного плейсхолдера на то что нужно.


А, собственно все, спасибо за внимание. Как допилю всё - закину сюда ссылочку или может еще чего напишу. Там я еще со сборщиком Parcel'ем развлекаюсь, есть о чем по бухтеть.