Русский
yield_blocks: CSS и JS по страницам
yield_blocks собирает CSS (или JS) из компонентов, использованных на странице, и выводит его один раз, инлайн. На страницу попадают только стили тех компонентов, которые на ней есть.
Проблема, которую это решает
При сборке страницы из переиспользуемых компонентов у каждого компонента есть свой CSS. Глобальный файл стилей загружает всё на каждой странице. yield_blocks работает иначе: сканирует, какие компоненты есть на странице, и записывает только их CSS в один тег <style>.
Структура файлов
Компоненты хранятся рядом с файлами шаблонов. Каждый файл компонента содержит его блоки:
_layouts/
└── my-theme/
├── components/
│ ├── button.html
│ ├── card.html
│ └── hero.html
└── page.html
Структура файла компонента
Файл компонента содержит два вида блоков: блок стилей и HTML-блок. Оба следуют соглашению об именовании:
_style_имякомпонента— CSS компонента_js_имякомпонента— JS компонента (опционально)имякомпонента— HTML компонента
<!-- components/button.html -->
{{block _style_button()}}
.button { display: inline-flex; padding: 8px 16px; }
.button--primary { background: #0070f3; color: #fff; }
{{end}}
{{block button(label="Нажмите", variant="")}}
<button class="button{{if variant}} button--{{variant}}{{end}}">{{label}}</button>
{{end}}
Имя блока стилей начинается с _style_. HTML-блок использует простое имя компонента. Загрузчик сопоставляет их по общему суффиксу (button в _style_button и button).
Использование компонентов на странице
Шаблон страницы вызывает компоненты через {{yield}}. Импорты не нужны — загрузчик находит файлы компонентов автоматически.
<!-- pages/home.html -->
<html>
<head>
<style>{{yield_blocks("_style_")}}</style>
</head>
<body>
{{yield hero(title="Добро пожаловать")}}
{{yield card(title="Возможности", body="...")}}
{{yield button(label="Начать", variant="primary")}}
</body>
</html>
yield_blocks("_style_") сканирует все вызовы {{yield}} на странице, находит каждый блок, чьё имя начинается с _style_, и записывает их CSS в тег. Результат — один <style> только с тем, что использует эта страница.
Разместите <style>{{yield_blocks("_style_")}}</style> в <head>. Имена блоков определяются статическим анализом при загрузке шаблона — до начала рендеринга, — поэтому вызов корректно работает в <head> и предотвращает мелькание нестилизованного контента (FOUC) при первой отрисовке.
Синтаксис паттернов
yield_blocks принимает строку-префикс или регулярное выражение:
| Паттерн | Что совпадает |
|---|---|
"_style_" |
Все блоки, чьё имя начинается с _style_ |
"/_style_.*/" |
То же, как регулярное выражение (в обёртке /) |
"/_js_.*/" |
Все блоки, чьё имя начинается с _js_ |
Для CSS и JS используйте форму с префиксом — она короче и читается понятнее.
Для JS добавьте тег <script> аналогичным образом:
<script>{{yield_blocks("/_js_.*/")}}</script>
Транзитивные зависимости
Если card.html внутри себя вызывает button, загрузчик автоматически включает button.html. Перечислять зависимости вручную не нужно — загрузчик обходит полный граф компонентов.
Уникальность имён блоков и @lid / @did
Имена блоков в Jet глобальны для всех включённых файлов. Если hero.html и card.html оба определяют {{block hero()}}, второе определение молча перезапишет первое. В проектах с несколькими компонентами, которые разрабатываются независимо, это реальная проблема.
@lid решает её: перед разбором шаблона плейсхолдер автоматически заменяется значением, производным от пути к файлу. Никакой настройки не требуется.
| Путь к файлу | Значение @lid |
Значение @did |
|---|---|---|
button.html |
button |
button |
components/button.html |
components_button |
components-button |
ui/nav/header.html |
ui_nav_header |
ui-nav-header |
@lid и @did — переменные препроцессора: загрузчик подставляет их до того, как Jet разбирает шаблон. @lid (lodash id — подчёркивания) используется в именах блоков Jet. @did (dash id — дефисы) используется в BEM CSS-классах. Оба значения берутся из пути к файлу, а не из имени блока. Для вставки литерального @lid / @did в JS или CSS используйте @@lid / @@did.
Используйте @lid в именах блоков и @did в CSS-классах:
{{block _style_@lid()}}
.@did { display: inline-flex; padding: 8px 20px; }
.@did--primary { background: #0070f3; color: #fff; }
{{end}}
{{block @lid(label="Нажмите", variant="")}}
<button class="@did{{if variant}} @did--{{variant}}{{end}}">{{label}}</button>
{{end}}
Для button.html это раскрывается в _style_button, .button и button. Для components/button.html — в _style_components_button, .components-button и components_button. Паттерн yield_blocks("_style_") по-прежнему найдёт оба варианта, потому что префикс проверяется по уже раскрытому имени.
Экранирование. Если в выводе нужна буквальная строка @lid — например, как переменная JavaScript — напишите @@lid:
{{block _js_@lid()}}
var @@lid = document.querySelector('.@did-root');
@@lid.addEventListener('click', () => { ... });
{{end}}
После подстановки (для button.html):
{{block _js_button()}}
var @lid = document.querySelector('.button-root');
@lid.addEventListener('click', () => { ... });
{{end}}
@@lid превращается в @lid в выводе; @lid (одиночный @) — в значение, производное от пути файла.
Предупреждения
Два случая дают нефатальное предупреждение в логе предпросмотра шаблона:
- Дублирование имени блока — два файла компонентов определяют блок с одним именем. Побеждает первое определение, второе игнорируется.
- Невалидное регулярное выражение — паттерн в обёртке
/, который не является корректным regexp.yield_blocksвыводит пустую строку для этого вызова; остальная часть страницы рендерится нормально.
Ни одно из предупреждений не прерывает рендеринг. Если стили неожиданно пропали — проверьте лог предпросмотра шаблона.
Правила для CSS-блоков
Параметры блоков не передаются. yield_blocks вызывает каждый CSS-блок без аргументов. Все параметры получают нулевые/пустые значения. Блок вида {{block _style_@lid(theme="")}}{{if theme}}.box--{{theme}}...{{end}}{{end}} никогда не выведет условную часть. CSS-блоки должны быть самодостаточными — не ветвитесь на основе собственных параметров.
Глобальные функции работают нормально. Глобалы вроде asset() и note доступны внутри CSS-блоков, вызванных через yield_blocks, точно так же, как в любом другом блоке.
Не вызывайте соседние CSS-блоки вручную. Если CSS-блок внутри себя делает {{yield _style_other()}}, а _style_other тоже попадает под паттерн yield_blocks, этот CSS выведется дважды. Пусть сбором занимается yield_blocks — CSS-блоки не должны вызывать друг друга.
Полный пример
_layouts/theme/
├── components/
│ ├── button.html
│ ├── card.html
│ └── hero.html
└── page.html
components/hero.html:
{{block _style_hero()}}
.hero { padding: 80px 24px; text-align: center; }
.hero__title { font-size: 2.5rem; font-weight: 700; }
{{end}}
{{block hero(title="")}}
<section class="hero">
<h1 class="hero__title">{{title}}</h1>
</section>
{{end}}
page.html:
<!DOCTYPE html>
<html>
<head>
<style>{{yield_blocks("_style_")}}</style>
</head>
<body>
{{yield hero(title=note.Title())}}
{{yield card(title="О проекте", body=note.HTMLString())}}
</body>
</html>
На отрендеренной странице тег <style> содержит CSS из _style_hero и _style_card. _style_button отсутствует — на этой странице нет {{yield button(...)}}.
BEM-именование для блоков стилей
Соглашение _style_имяблока хорошо сочетается с BEM-именованием классов. BEM привязывает каждый класс к имени блока, поэтому когда yield_blocks собирает CSS из нескольких компонентов на одной странице, коллизий не возникает.
Компонент card с BEM-классами:
{{block _style_card()}}
.card { border: 1px solid #eee; border-radius: 6px; padding: 16px; }
.card__title { font-weight: bold; }
.card--featured { border-color: #0070f3; }
{{end}}
.card__title никогда не совпадёт с .hero__title — имя блока входит в каждый класс. Полное описание соглашения — в BEM-именование в шаблонах.
Статические файлы: asset()
Шаблон может ссылаться на статические файлы (JS, CSS, SVG, изображения), лежащие рядом с ним, через asset():
<script defer src="{{ asset("scripts.js") }}"></script>
<img src="{{ asset("logo.svg") }}" alt="logo">
asset() разрешает путь относительно директории самого шаблона. Если шаблон находится по пути _layouts/mesh/index.html, то {{ asset("scripts.js") }} отдаёт файл _layouts/mesh/scripts.js.
В продакшне URL содержит хеш для сброса кеша, чтобы браузеры подхватывали обновления немедленно. В режиме разработки хеш опускается для читаемости.
Рядом с шаблоном можно разместить любой статический файл — JS, SVG, изображения, шрифты. Движок отдаёт их автоматически, без шага сборки и дополнительной конфигурации.
Пример структуры шаблона:
_layouts/
└── mesh/
├── _blocks.html
├── index.html
├── scripts.js ← {{ asset("scripts.js") }}
└── logo.svg ← {{ asset("logo.svg") }}
Отличие от yield_blocks: тот собирает инлайновые CSS/JS из блоков компонентов во время рендеринга. asset() используется, когда файл является самостоятельным статическим ресурсом, которому шаблонная обработка не нужна.
Смотрите также
- Шаблоны — основы шаблонов, синтаксис Jet, переменные
noteиnvs - Лучшие практики шаблонов — организация проектов с несколькими шаблонами
- BEM-именование в шаблонах — соглашение об именовании CSS-классов компонентов