Субъективная скорость зачастую важна не меньше, чем реальная производительность сервера. На типичной странице Drupal несколько фрагментов — ссылки аккаунта, меню, панели инструментов, персонализированные виджеты — зависят от пользователя или сессии, тогда как остальная вёрстка может быть закэширована и отдана сразу. Плейсхолдеры и BigPipe — способ Drupal разделить эти задачи: отложить дорогие участки, сначала отправить каркас и заполнить пробелы, когда они будут готовы.
Ключевые понятия
Прежде чем разбирать конфигурационные файлы и render arrays, полезно зафиксировать три термина. В разговоре их часто путают, но в Drupal они описывают разные уровни одного и того же конвейера.
- Плейсхолдер — временная заглушка в render array или в HTML, отправляемом в браузер. Он помечает поддерево, которое Drupal построит позже.
- Lazy builder — callback, который Drupal вызывает, чтобы сгенерировать реальный контент для этого плейсхолдера.
- BigPipe — стратегия доставки плейсхолдеров. При выполнении условий Drupal сначала отправляет каркас страницы, а затем потоково передаёт разметку замены для выбранных плейсхолдеров.
Модель, которая снимает большинство путаницы, последовательная, а не синонимичная:
- Drupal собирает render array для страницы.
- Рендерер может преобразовать части этого массива в плейсхолдеры.
- Если запрос подходит, BigPipe доставляет эти плейсхолдеры прогрессивно, не блокируя весь ответ.
BigPipe — это не сам механизм плейсхолдеров. Placeholdering относится к Render API; BigPipe — слой прогрессивной доставки, который может работать поверх него.
Как устроен конвейер рендеринга
Каждый контроллер, блок, entity view builder и field formatter в итоге формирует render arrays. По ходу рендерер проверяет, можно ли отложить поддерево, нужно ли превратить его в плейсхолдер и разрешено ли BigPipe передавать результат потоково. Диаграмма ниже показывает эти точки принятия решений.
Что запускает создание плейсхолдеров
Drupal обычно требует #lazy_builder в поддереве render array, прежде чем placeholdering станет возможным. Когда callback уже есть, создание плейсхолдера можно запросить явно, получить через block plugin или запустить автоматически при плохой кэшируемости.
Block Plugin API
Block plugins могут подключиться к пути плейсхолдеров через отдельный метод в BlockPluginInterface. Значение по умолчанию в BlockPluginTrait — FALSE; отдельные блоки переопределяют его, когда отложенный рендеринг оправдан.
public function createPlaceholder(): bool {
return TRUE;
}Этот API относится только к block plugins — не к сущностям, полям или нодам в целом. В ядре Drupal SystemMenuBlock возвращает TRUE, поэтому блоки навигации изначально совместимы с плейсхолдерами. Их вывод часто зависит от активного следа меню и текущего маршрута — контекстов кэша, которые плохо укладываются в одну общую запись кэша страницы.
Generic Render API
Любой код, возвращающий render array, может отложить поддерево с помощью #lazy_builder и явно запросить преобразование в плейсхолдер:
$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:
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-операций. Оба подхода могут сосуществовать на одной странице.
Нужен эксперт по 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. Обработка блоков и меню в ядре уже участвует в конвейере плейсхолдеров, когда кэшируемость это оправдывает.
Рабочие паттерны кода
Только lazy builder
Объявление lazy builder делает отложенный рендеринг возможным, но само по себе не принуждает к созданию плейсхолдера. Auto-placeholdering всё равно может сработать, если метаданные кэшируемости совпадают с настроенными условиями.
$build['links'] = [
'#lazy_builder' => [
static::class . '::renderLinks',
[$entity->id(), $view_mode],
],
];Lazy builder с явным плейсхолдером
Самый прямой универсальный паттерн, когда вы знаете, что поддерево должно заменяться асинхронно.
$build['links'] = [
'#lazy_builder' => [
static::class . '::renderLinks',
[$entity->id(), $view_mode],
],
'#create_placeholder' => TRUE,
];Поддержка плейсхолдеров в block plugins
В block plugins переопределение createPlaceholder() указывает block view builder Drupal направить блок по пути плейсхолдеров.
public function createPlaceholder(): bool {
return TRUE;
}Пример callback lazy builder
Сам callback возвращает render array, как и любой другой builder. Метаданные кэша этого массива определяют, как сохраняется итоговый вывод и применяется ли auto-placeholdering на предыдущих этапах.
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 определяет, как это приходит.