Pug — подключение файлов, блоки, миксины (includes, bloks, mixins)

Pug - Includes, Extends & Blocks, Mixin
  1. Includes
  2. Extends & Block
  3. Mixins

Подключение файлов

В Pug можно вставлять содержимое одного файла в другой. Это позволяет выносить в отдельные файлы разметку повторяющихся блоков или миксины. Возьмём два файла, один из которых содержит разметку для head, а второй разметку для footer

//- includes/head.pug
head
  title My Site
  script(src='/javascripts/jquery.js')
  script(src='/javascripts/app.js')
//- includes/foot.pug
footer#footer
  p Copyright (c) foobar

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

//- index.pug
doctype html
html
  include includes/head.pug
  body
    h1 My Site
    p Welcome to my super lame site.
    include includes/foot.pug

По итогу получим такой HTML

<!DOCTYPE html>
<html>

<head>
  <title>My Site</title>
  <script src="/javascripts/jquery.js"></script>
  <script src="/javascripts/app.js"></script>
</head>

<body>
  <h1>My Site</h1>
  <p>Welcome to my super lame site.</p>
  <footer id="footer">
    <p>Copyright (c) foobar</p>
  </footer>
</body>
</html>

Если в include указан абсолютный путь (include /root.pug), то корневой каталог должен быть определён в options.basedir. Для относительных путей адрес определяется относительно файла в котором идёт подключение. Если расширение файла не указано, то по умолчанию подставляется .pug

Не Pug файлы подключаются как текст

/* style.css */
h1 {
  color: red;
}
// script.js
console.log('You are awesome');

Подключим css и js файлы в Pug

//- index.pug
doctype html
html
  head
    style
      include style.css
  body
    h1 My Site
    p Welcome to my super lame site.
    script
      include script.js

В итоге получим таrой HTML

<!DOCTYPE html>
<html>

<head>
  <style>
    /* style.css */

    h1 {
      color: red;
    }
  </style>
</head>

<body>
  <h1>My Site</h1>
  <p>Welcome to my super lame site.</p>
  <script>
    // script.js
    console.log('You are awesome');
  </script>
</body>
</html>

Можно комбинировать фильтры с include, позволяя фильтровать содержимое файлов по мере их включения. Попробуем вставить файл с markdown разметкой

# article.md
This is an article written in markdown.
//- index.pug
doctype html
html
  head
    title An Article
  body
    include:markdown-it article.md
<!DOCTYPE html>
<html>
  <head>
    <title>An Article</title>
  </head>
  <body>
    <h1>article.md</h1>
    <p>This is an article written in markdown.</p>
  </body>
</html>

Шаблоны блоки расширения

Pug поддерживает наследование шаблонов. Для этого используют ключевые слова block и extends. Блоки шаблона могут быть заменены в дочернем шаблоне. Блоки в Pug могут содержать контент по умолчанию. В приведенном ниже примере, в макете, определяются block scripts, block content и block foot.

//- layout.pug
html
  head
    title My Site - #{title}
    block scripts
      script(src='/jquery.js')
  body
    block content
    block foot
      #footer
        p some footer content

Чтобы применить этот макет в другом файле используем extends и укажем путь к шаблону(если не указать расширение файла, .pug будет добавлен автоматически). Подключив шаблон можно переопределить некоторые блоки. В примере ниже блок foot не переопределяется, поэтому его содержимое останется как в родительском шаблоне.

//- page-a.pug
extends layout.pug

block scripts
  script(src='/jquery.js')
  script(src='/pets.js')

block content
  h1= title
  - let pets = ['cat', 'dog']
  each petName in pets
    include pet.pug
//- pet.pug
p= petName

Результат

<!DOCTYPE html>
<html>
  <head>
    <title>My Site - </title>
    <script src="/jquery.js"></script>
    <script src="/pets.js"></script>
  </head>
  <body>
    <h1></h1>
    <p>cat</p>
    <p>dog</p>
    <div id="footer">
      <p>some footer content</p>
    </div>
  </body>
</html>

Переопределяя блок можно поместить в него другие блоки. В следующем примере, при переопределении блока content в него были добавлены блоки sidebar и primary

//- sub-layout.pug
extends layout.pug

block content
  .sidebar
    block sidebar
      p nothing
  .primary
    block primary
      p nothing

append/prepend

В Pug можно переопределять блоки добавляя в них контент и не удаляя содержимое. Предположим есть скрипты которые должны подключаться на каждой странице. Тогда шаблон будет выглядеть следующим образом

//- layout.pug
html
  head
    block head
      script(src='/vendor/jquery.js')
      script(src='/vendor/caustic.js')
  body
    block content

У нас есть страница с Javascript игрой и для неё нужны свои скрипты. С помощью append/prepend можно добавить их не удаляя скрипты из родительского шаблона.

//- page.pug
extends layout

append head
  script(src='/vendor/three.js')
  script(src='/game.js')

Когда используем append/prepend можно не писать слово block.  Вместо block append head достаточно append head.

Частые ошибки в использовании шаблонов

Наследование шаблонов Pug - это мощная функция, которая позволяет разбивать сложные структуры шаблонов страниц на более мелкие и простые файлы, но не стоит объединять слишком много шаблонов вместе.

Обратите внимание, что только блоки и определения миксинов могут отображаться на верхнем уровне(без отступа) дочернего шаблона. Это важно! Родительские шаблоны определяют общую структуру данных, а дочерние шаблоны могут только изменять определённые блоки в разметке и логику. Если дочерний шаблон попытается добавить контент вне блока, Pug не будет знать куда поместить его на странице.

Небуферизованный код, который может содержаться в разметке. Если вам нужно определить переменные для использования в дочернем шаблоне, вы можете сделать это несколькими различными способами:

  • Добавьте переменные в объект параметров Pug или определите их в небуферизованном коде в родительском шаблоне. Дочерний шаблон наследует эти переменные.
  • Определите переменные в блоке в дочернем шаблоне. Расширяемые шаблоны должны иметь хотя бы один блок, иначе он будет пустым - определите свои переменные там.

Буферизованные комментарии Pug не могут отображаться на верхнем уровне расширяемого шаблона: они создают комментарии HTML, которые не будут помещаться в получившийся HTML. (Небуферизованные комментарии Pug могут располагаться где угодно.)

Миксины

С помощью миксинов можно создавать переиспользуемые блоки

 //- Declaration
mixin list
  ul
    li foo
    li bar
    li baz
//- Use
+list
+list
ul>
  <li>foo</li>
  <li>bar</li>
  <li>baz</li>
</ul>
<ul>
  <li>foo</li>
  <li>bar</li>
  <li>baz</li>
</ul>

Миксины могут принимать аргументы

mixin pet(name)
  li.pet= name
ul
  +pet('cat')
  +pet('dog')
  +pet('pig')
<ul>
  <li class="pet">cat</li>
  <li class="pet">dog</li>
  <li class="pet">pig</li>
</ul>

Миксин может принимать блок Pug и действовать в зависимости от содержания

mixin article(title)
  .article
    .article-wrapper
      h1= title
      if block
        block
      else
        p No content provided

+article('Hello world')

+article('Hello world')
  p This is my
  p Amazing article
<div class="article">
  <div class="article-wrapper">
    <h1>Hello world</h1>
    <p>No content provided</p>
  </div>
</div>
<div class="article">
  <div class="article-wrapper">
    <h1>Hello world</h1>
    <p>This is my</p>
    <p>Amazing article</p>
  </div>
</div>

Миксины могут получать не явные attributes аргументы, которые берутся из атрибутов, переданных в миксин.

mixin link(href, name)
  //- attributes == {class: "btn"}
  a(class!=attributes.class href=href)= name

+link('/foo', 'foo')(class="btn")
<a class="btn" href="/foo">foo</a>

Значения в атрибутах, по умолчанию уже экранированы! Используйте ! =, чтобы избежать их повторного экранирования.

Можно использовать миксин с &atributes

mixin link(href, name)
  a(href=href)&attributes(attributes)= name

+link('/foo', 'foo')(class="btn")
<a class="btn" href="/foo">foo</a>

Синтаксис +link(class = "btn") допустим и эквивалентен + link()(class = "btn"), поскольку Pug пытается определить, является ли содержимое скобок атрибутами или аргументами. Тем не менее рекомендуется использовать второй синтаксис, так как вы явно не передаете аргументы и подтверждаете, что первая скобка - это список аргументов.

В миксине можно устанавливать аргументы по умолчанию

//- Declaration
mixin article(title='Default Title')
  .article
    .article-wrapper
      h1= title

//- Use
+article()

+article('Hello world')
<div class="article">
  <div class="article-wrapper">
    <h1>Default Title</h1>
  </div>
</div>
<div class="article">
  <div class="article-wrapper">
    <h1>Hello world</h1>
  </div>
</div>

Вы можете написать миксины, которые принимают неизвестное количество аргументов, используя синтаксис «rest arguments».

mixin list(id, ...items)
  ul(id=id)
    each item in items
      li= item

+list('my-list', 1, 2, 3, 4)
<ul id="my-list">
  <li>1</li>
  <li>2</li>
  <li>3</li>
  <li>4</li>
</ul>