Перейти к основному содержанию
Login

BigPipe и плейсхолдеры в Drupal

Andrii Kocherhin profile picture
Автор: Andrii Kocherhin
Fluidity abstract website

Субъективная скорость зачастую важна не меньше, чем реальная производительность сервера. На типичной странице Drupal несколько фрагментов — ссылки аккаунта, меню, панели инструментов, персонализированные виджеты — зависят от пользователя или сессии, тогда как остальная вёрстка может быть закэширована и отдана сразу. Плейсхолдеры и BigPipe — способ Drupal разделить эти задачи: отложить дорогие участки, сначала отправить каркас и заполнить пробелы, когда они будут готовы.

 
Earth at night with glowing network lines, symbolizing progressive web content streaming across a page shell
Земля ночью со светящимися линиями сети — символ потоковой подачи веб-контента поверх каркаса страницы. Фото: NASA на Unsplash

Ключевые понятия

Прежде чем разбирать конфигурационные файлы и render arrays, полезно зафиксировать три термина. В разговоре их часто путают, но в Drupal они описывают разные уровни одного и того же конвейера.

  • Плейсхолдер — временная заглушка в render array или в HTML, отправляемом в браузер. Он помечает поддерево, которое Drupal построит позже.
  • Lazy builder — callback, который Drupal вызывает, чтобы сгенерировать реальный контент для этого плейсхолдера.
  • BigPipe — стратегия доставки плейсхолдеров. При выполнении условий Drupal сначала отправляет каркас страницы, а затем потоково передаёт разметку замены для выбранных плейсхолдеров.

Модель, которая снимает большинство путаницы, последовательная, а не синонимичная:

  1. Drupal собирает render array для страницы.
  2. Рендерер может преобразовать части этого массива в плейсхолдеры.
  3. Если запрос подходит, BigPipe доставляет эти плейсхолдеры прогрессивно, не блокируя весь ответ.

BigPipe — это не сам механизм плейсхолдеров. Placeholdering относится к Render API; BigPipe — слой прогрессивной доставки, который может работать поверх него.

Как устроен конвейер рендеринга

Каждый контроллер, блок, entity view builder и field formatter в итоге формирует render arrays. По ходу рендерер проверяет, можно ли отложить поддерево, нужно ли превратить его в плейсхолдер и разрешено ли BigPipe передавать результат потоково. Диаграмма ниже показывает эти точки принятия решений.

Flowchart showing Drupal render array paths from lazy builder through placeholder creation to BigPipe progressive delivery
Блок-схема пути render array в Drupal: от lazy builder через создание плейсхолдера к прогрессивной доставке BigPipe.

Что запускает создание плейсхолдеров

Drupal обычно требует #lazy_builder в поддереве render array, прежде чем placeholdering станет возможным. Когда callback уже есть, создание плейсхолдера можно запросить явно, получить через block plugin или запустить автоматически при плохой кэшируемости.

Block Plugin API

Block plugins могут подключиться к пути плейсхолдеров через отдельный метод в BlockPluginInterface. Значение по умолчанию в BlockPluginTraitFALSE; отдельные блоки переопределяют его, когда отложенный рендеринг оправдан.

php
public function createPlaceholder(): bool {
  return TRUE;
}

Этот API относится только к block plugins — не к сущностям, полям или нодам в целом. В ядре Drupal SystemMenuBlock возвращает TRUE, поэтому блоки навигации изначально совместимы с плейсхолдерами. Их вывод часто зависит от активного следа меню и текущего маршрута — контекстов кэша, которые плохо укладываются в одну общую запись кэша страницы.

Generic Render API

Любой код, возвращающий render array, может отложить поддерево с помощью #lazy_builder и явно запросить преобразование в плейсхолдер:

php
$build['dynamic_part'] = [
  '#lazy_builder' => [static::class . '::lazyPart', [$id]],
  '#create_placeholder' => TRUE,
];

Это универсальный паттерн, работающий вне Block API. Типичные точки интеграции: массивы build() блоков, entity view builders, field formatters, contextual link builders и пользовательские контроллеры.

Установка #create_placeholder в FALSE явно запрещает placeholdering, даже если lazy builder присутствует. Это полезно, когда нужна семантика отложенного рендеринга без пути доставки через плейсхолдеры.

Auto-placeholdering

Даже без #create_placeholder => TRUE Drupal может автоматически превращать поддеревья с lazy builder в плейсхолдеры, когда метаданные кэшируемости совпадают с настроенными порогами. Значения по умолчанию находятся в sites/default/default.services.yml в секции renderer.config:

yaml
renderer.config:
  auto_placeholder_conditions:
    max-age: 0
    contexts: ['session', 'user']
    tags: []

На практике auto-placeholdering срабатывает, когда у элемента с lazy builder max-age не выше заданного лимита (по умолчанию 0), когда его контексты кэша пересекаются с настроенным списком (session и user по умолчанию) или когда его cache tags совпадают с настроенными тегами (по умолчанию пусто). Отдельные условия можно настроить или отключить: установите max-age в -1 или contexts в [].

Auto-placeholdering не означает, что вся страница проходит через BigPipe. Он затрагивает только отдельные поддеревья, которые уже подходят для placeholdering и несут метаданные кэшируемости, соответствующие этим правилам.

Когда подключается BigPipe

После появления плейсхолдеров отдельный набор условий определяет, будет ли BigPipe передавать их замены потоково или Drupal разрешит их в стандартном потоке с одним ответом. Placeholdering и BigPipe связаны, но это независимые решения.

Согласно BigPipeStrategy в ядре Drupal, BigPipe применяется, когда выполняются все следующие условия:

  • Запрос использует кэшируемый метод, обычно GET (некэшируемые методы вроде POST исключаются, чтобы формы внутри плейсхолдеров обрабатывались сразу).
  • Маршрут не отказался от BigPipe через опцию _no_big_pipe.
  • Запрос связан с сессией. Без сессии Drupal считает, что ответ не является по-настоящему динамическим, и отдаёт анонимных посетителей внутреннему кэшу страниц. BigPipe всё равно полезен анонимным пользователям с сессией — классический пример: корзина покупок.
  • Запрос не является sub-request. BigPipe не может безопасно обрабатывать плейсхолдеры, отрендеренные внутри sub-request, потому что стек запросов отличается при рендеринге замен.

Когда BigPipe активируется, он использует одну из двух подстратегий. При включённом JavaScript замены передаются в конце страницы и подставляются в DOM — как правило, лучший вариант по воспринимаемой скорости. При наличии cookie big_pipe_nojs или для плейсхолдеров на уровне атрибутов, которые нельзя эффективно найти через querySelector, замены передаются на месте через несколько flush-операций. Оба подхода могут сосуществовать на одной странице.

Server racks in a data center, representing the backend rendering layer that prepares page shells and deferred fragments
Серверные стойки в дата-центре — метафора backend-слоя, который готовит каркас страницы и отложенные фрагменты. Фото: Taylor Vick на Unsplash

Нужен эксперт по Drupal?

Echo Flow помогает канадским компаниям с Drupal-разработкой корпоративного уровня.

Пример из практики: навигация в шапке

Распространённый паттерн на контентных сайтах Drupal показывает, как эти элементы складываются в продакшене. Представьте сайт, где модуль big_pipe включён в профиле установки — он входит в ядро Drupal и по умолчанию активен в профиле Standard.

Тема выводит основную навигацию в шапке страницы, часто рендеря блок меню, размещённый в регионе header в page.html.twig. Поскольку SystemMenuBlock подключается к placeholdering, а его контексты кэша зависят от активного следа, авторизованные посетители могут получить быстрый каркас страницы, пока разметка меню ещё строится.

Frontend-коду иногда нужно учитывать эту задержку. Компонент темы в шапке может слушать Drupal behaviors или отслеживать обновления DOM после того, как BigPipe внедрит финальный HTML меню — например, чтобы инициализировать мобильный переключатель, пересчитать sticky-offset или привязать навигацию с клавиатуры, когда ссылки уже на месте. Такой «защитный» JavaScript — практический признак того, что работает рендеринг через плейсхолдеры, даже если для проекта не писали собственный lazy builder.

Для этого паттерна не нужен кастомный lazy builder. Обработка блоков и меню в ядре уже участвует в конвейере плейсхолдеров, когда кэшируемость это оправдывает.

Рабочие паттерны кода

PHP code on a laptop screen in a developer workspace, illustrating Drupal render array and lazy builder patterns
PHP-код на экране ноутбука в рабочем пространстве разработчика — иллюстрация паттернов render array и lazy builders в Drupal. Фото: Lukas Blazek на Unsplash

Только lazy builder

Объявление lazy builder делает отложенный рендеринг возможным, но само по себе не принуждает к созданию плейсхолдера. Auto-placeholdering всё равно может сработать, если метаданные кэшируемости совпадают с настроенными условиями.

php
$build['links'] = [
  '#lazy_builder' => [
    static::class . '::renderLinks',
    [$entity->id(), $view_mode],
  ],
];

Lazy builder с явным плейсхолдером

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

php
$build['links'] = [
  '#lazy_builder' => [
    static::class . '::renderLinks',
    [$entity->id(), $view_mode],
  ],
  '#create_placeholder' => TRUE,
];

Поддержка плейсхолдеров в block plugins

В block plugins переопределение createPlaceholder() указывает block view builder Drupal направить блок по пути плейсхолдеров.

php
public function createPlaceholder(): bool {
  return TRUE;
}

Пример callback lazy builder

Сам callback возвращает render array, как и любой другой builder. Метаданные кэша этого массива определяют, как сохраняется итоговый вывод и применяется ли auto-placeholdering на предыдущих этапах.

php
public static function lazyPart($id): array {
  return [
    '#markup' => 'Rendered later: ' . $id,
    '#cache' => [
      'contexts' => ['user'],
    ],
  ];
}

Поскольку вывод зависит от пользователя, это сильный кандидат для auto-placeholdering при условиях renderer.config по умолчанию.

Как выбрать подходящих кандидатов

BigPipe окупается, когда у страницы есть содержательный каркас, который держится сам по себе, пока небольшие динамические фрагменты приходят чуть позже. Цель — более быстрый first paint и отзывчивость, а не перенос каждой дорогой операции за плейсхолдер.

Хорошие кандидаты:

  • Панель инструментов или ссылки аккаунта, зависящие от пользователя
  • Контекстные ссылки и локальные задачи
  • Персонализированные виджеты в боковой панели
  • Сохранённые или недавно просмотренные фрагменты
  • Вторичные элементы управления на странице поиска или листинга

Слабые кандидаты:

  • Весь основной список результатов поиска
  • Вся область основного контента
  • Крупные фрагменты, без которых начальная страница выглядит пустой или сломанной

На тяжёлых страницах поиска BigPipe может помочь с периферийными динамическими фрагментами, но редко становится первым рычагом для медленного списка результатов. Начните с стоимости запросов, настройки Views, тюнинга поискового бэкенда и накладных расходов рендеринга строк. Drupal 11.3 также принёс более широкие оптимизации render cache и плейсхолдеров, снижающие нагрузку на многих страницах — имеет смысл обновиться, прежде чем искать кастомные обходные пути через BigPipe.

Краткая справка

  • #lazy_builder — это поддерево можно построить позже через указанный callback.
  • #create_placeholder — превратить поддерево в плейсхолдер (установите FALSE, чтобы отказаться).
  • createPlaceholder() — вспомогательный метод block plugin; по умолчанию FALSE в BlockPluginTrait.
  • Auto-placeholdering — настраивается в renderer.config; срабатывает по max-age, контекстам кэша и cache tags.
  • BigPipe — потоково передаёт замены подходящих плейсхолдеров, когда запрос кэшируемый, есть сессия, это не sub-request и маршрут не отказался от BigPipe.

Заключение

Placeholdering — механизм на этапе рендеринга, который позволяет Drupal откладывать работу, не блокируя всю страницу. BigPipe — стратегия доставки, которая может потоково передавать отложенные фрагменты после того, как каркас дошёл до браузера. Понимание того, где принимается каждое решение — lazy builders, явное или автоматическое создание плейсхолдеров и условия BigPipe — сильно упрощает отладку медленных страниц для авторизованных пользователей, проектирование блоков с разумными метаданными кэша и выбор фрагментов, которые стоит откладывать.

Поддерживаете ли вы кастомный блок, настраиваете шапку темы или профилируете сложную View — базовая формулировка остаётся верной: placeholdering определяет, что откладывается; BigPipe определяет, как это приходит.