Зарегистрируйтесь, чтобы продолжить обучение

Миксины HTML: Препроцессор Pug

При разработке сайта количество одинаковых по структуре блоков велико. Представьте каталог интернет-магазина. Он может состоять из сотен карточек, которые отличаются только данными. При разработке «по старинке», без шаблонизаторов, все эти карточки верстаются методом «копировать-вставить». Не идеальное решение, но вполне рабочее. Именно так кажется до тех пор, пока не произойдет изменение карточек. Любое добавление/удаление элементов приведет к часам, а то и дням работы по изменению верстки.

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

Для переиспользования верстки в 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>

Если таких статей много на сайте, то есть смысл вынести шаблон в отдельный миксин. Но как же передать данные? Тут есть два пути:

  1. Определить аргументы, и передавать их отдельно в миксин.
  2. Каким-то образом сразу передать всю статью.

Первый способ может быть неудобен из-за тела статьи. Ведь статья не является произведением из одного предложения, которое можно передать в виде аргумента миксина. К тому же статьи зачастую хранятся в отдельном файле, как было в уроке про 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>

Дополнительное задание

Доработайте миксин с добавлением статьи на страницу:

  1. Проверьте, что существует контент для отображения. Если его нет, то выведите ошибку
  2. Добавьте значения по умолчанию для аргументов. В качестве имени статьи используйте «Hexlet». В качестве автора «Кодовенок»

Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff

Используйте Хекслет по-максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»