Перейти к содержимому

SSR-блоки

Блок становится SSR-блоком автоматически: достаточно объявить в схеме bindingSlots. Отдельного флага нет.

Разница принципиальная. Обычный блок PageCraft рендерит сам и отдаёт витрине готовый HTML. SSR-блок отдаётся шаблоном: витрина добывает данные выбранного источника, подставляет их в data.<слот> и рендерит шаблон у себя — уже с реальным содержимым магазина и в момент запроса страницы посетителем.

Шаблон блока заворачивается в <template> внутри контейнера-якоря, по которому рантайм витрины его находит:

<div data-block-wrapper-id="4218" style="display: contents">
<template data-block-id="4218">
<h2>{{ props.title }}</h2>
<ul>{% for product in data.products.items %}…{% endfor %}</ul>
</template>
</div>

Рядом с HTML страницы уходит контекст — по записи на каждый SSR-блок:

{
"version": 1,
"blocks": {
"4218": {
"type": "Карточки товаров",
"version": "2.1",
"props": { "title": "Хиты продаж" },
"binding": {
"products": { "source": "catalog.products", "params": { "limit": 8 } }
}
}
}
}

Ключ — идентификатор вхождения блока на странице, он же data-block-id. binding здесь уже разрешён: пустые слоты дозаполнены дефолтами схемы, а слоты, чей контекстный источник на этом виде документа не работает, из binding исключены — шаблон получит по ним null. Маркера контекстности в публикуемом теле нет: формат остаётся { source, params }, а данные из окружения источник добывает на витрине сам.

CSS и JS SSR-блока собираются и публикуются обычным порядком, вместе со стилями остальных блоков страницы, — на них SSR никак не влияет.

Если SSR-блок лежит в слоте контейнера, он уезжает <template>-ом внутри <template> родителя, а в context попадает наравне с блоками верхнего уровня: структура context плоская, по ней нельзя понять, что один блок внутри другого. Вложенность выражена только вёрсткой, и разбирает её рантайм витрины — изнутри наружу.

Два следствия для автора шаблона:

  • вложенный SSR-блок получает данные по своей записи в context, независимо от родителя. Контейнер не «раздаёт» ему элементы своей коллекции: такой механизм — отдельная возможность, и её пока нет;
  • контейнер сам может быть SSR-блоком. Тогда PageCraft подставляет в его шаблон только {{ slots.<имя> }} — собранный HTML детей, — а всё остальное уезжает нерендеренным, как у обычного SSR-блока.

Блок, зависящий от корзины, должен переставать врать сразу после её изменения. Для этого в схеме объявляется client.refreshOn:

{
"bindingSlots": {
"cart": { "resultType": "cart_summary", "allowedSources": ["cart.current"] }
},
"client": {
"refreshOn": ["cart.change"]
}
}

При публикации список превращается в атрибут контейнера (data-refresh-on="cart.change"), и рантайм витрины перерисовывает блок на сервере, подменяя его в DOM. Сейчас поддерживается одно событие — cart.change (состав или количество товаров в корзине изменились).

Указывайте только реально нужные события: каждая подписка — дополнительные SSR-запросы к витрине. Для блока без bindingSlots секция client смысла не имеет.

Дефолты полей в SSR-контекст не подставляются. У обычного блока пустые placeholder_values при рендере заменяются дефолтами схемы; в контекст SSR-блока props уезжают ровно такими, какими лежат в базе. Заполняйте placeholder_values явно.

Переменные сайта @имя в шаблоне SSR-блока не раскрываются. Подстановка идёт по отрендеренному HTML, а шаблон SSR-блока на этом этапе ещё не отрендерен и уезжает как есть. Всё, что зависит от переменных сайта, держите в обычных блоках или в макете.

Ошибки шаблона всплывут только на витрине. Локальный рендер SSR-блока не выполняется вовсе, а предпросмотр в редакторе идёт на заглушке типа результата — на одном аккуратном наборе данных. Пишите шаблон устойчивым: проверяйте пустой список, отсутствующие поля и нули.

{% if data.products.items.size > 0 %}
{% for product in data.products.items %}
<li>{{ product.name }}{{ product.variation.price.formatted | default: '—' }}</li>
{% endfor %}
{% else %}
<p>Товары не найдены</p>
{% endif %}

Клиентские запросы не нужны. Смысл SSR-блока в том, что данные приходят вместе с разметкой: поисковик видит товары, а посетитель не ждёт дозагрузки.

SSR-режим работает только там, где на стороне сайта есть рантайм, умеющий разворачивать <template data-block-id>. Сегодня это публикация в 4CMS. Публикация статикой по FTP рендерит все блоки на стороне PageCraft, поэтому блоку с bindingSlots там взяться данным неоткуда — для статических сайтов используйте обычные блоки.


Источники: backend/src/Publishing/BlockAssembler.php (renderBlocksWithSsr, wrapBindingBlock), backend/src/Publishing/Provider/FourCmsProvider.php (buildSsrContext), backend/src/Publishing/Provider/FtpProvider.php, backend/src/Publishing/BlockRenderer.php, backend/public/block-schema.json (client.refreshOn), backend/src/Service/Block/BlockBindingsResolver.php.