Проблема
Клиентские инструменты для A/B-тестирования загружают скрипт, который изменяет DOM после того, как браузер уже начал создавать страницу. Без вмешательства пользователь на короткое время видит оригинальный контент, прежде чем он мерцает или мигает, переходя к экспериментальному варианту. Раньше платформы для тестирования решали эту проблему с помощью фрагментов кода, которые скрывали всю страницу с помощью opacity: 0, пока не завершится выполнение скрипта эксперимента или не истечет время ожидания (обычно 4 секунды). При таком подходе невозможна прогрессивная отрисовка, могут происходить случайные клики по невидимому контенту и выполняются лишние циклы отрисовки.
Решение
Атрибут blocking=render позволяет элементу <script> или <link>, размещенному в <head>, блокировать отрисовку (но не синтаксический анализ) до тех пор, пока ресурс не будет получен и выполнен. Это дает скриптам экспериментов такое же поведение блокировки рендеринга, как и у таблиц стилей по умолчанию, гарантируя, что браузер никогда не отрисует страницу до тех пор, пока не будет применен вариант эксперимента. Никаких ухищрений с прозрачностью, произвольных тайм-аутов и мерцания.
Стратегия реализации
- ОБЯЗАТЕЛЬНО. Поместите скрипт эксперимента в элемент
<head>документа и добавьтеblocking="render". - ОБЯЗАТЕЛЬНО. Убедитесь, что в скрипте есть атрибут
type="module"(рекомендуется для встроенных скриптов) илиasync(рекомендуется для внешних скриптов со значениемsrc). - РЕКОМЕНДУЕТСЯ использовать небольшой и быстрый скрипт для экспериментов. Поскольку отрисовка блокируется до выполнения скрипта, большой или медленный скрипт напрямую задерживает первую отрисовку.
- НЕ ИСПОЛЬЗУЙТЕ
blocking="render"в скриптах, которые не нужно запускать до первой отрисовки. Он предназначен только для скриптов, результат работы которых должен быть виден при начальной отрисовке. - НЕ применяйте
blocking="render"к скриптам за пределами<head>. Блокировать отрисовку могут только скрипты в<head>.
Руководство по внедрению
Базовая настройка
ОБЯЗАТЕЛЬНО. Загрузите скрипт эксперимента с помощью async и blocking="render". Атрибут async гарантирует, что скрипт не заблокирует синтаксический анализ HTML (браузер продолжит создавать DOM во время получения скрипта). Атрибут blocking="render" гарантирует, что браузер не будет ничего отрисовывать до выполнения скрипта.
<head>
<!--
MANDATORY: Both `async` and `blocking="render"` are required.
- `async`: Prevents parser-blocking, so the DOM is built in parallel.
- `blocking="render"`: Holds rendering until the script executes,
ensuring experiment changes are applied before the user sees anything.
-->
<script
src="https://cdn.example.com/experiment-sdk.js"
async
blocking="render"
></script>
</head>
Загрузка стилей для эксперимента
Если для эксперимента требуется таблица стилей варианта, используйте blocking="render" в элементе <link>. Таблицы стилей в <head> по умолчанию блокируют отрисовку, но динамически внедренные таблицы стилей или таблицы, добавленные с помощью скрипта, этого не делают. Используйте blocking="render", когда таблица стилей добавляется динамически или когда вы хотите явно указать цель.
<head>
<!--
DO: Use blocking="render" on experiment stylesheets that are
dynamically inserted or conditionally loaded by the experiment SDK.
This ensures variant styles are applied before first paint.
-->
<link
rel="stylesheet"
href="https://cdn.example.com/experiment-variant-b.css"
blocking="render"
>
</head>
Встроенный скрипт эксперимента
Если логика эксперимента достаточно проста, чтобы встроить ее в код, используйте встроенный скрипт модуля с blocking="render". Это полезно, когда логика эксперимента извлекает конфигурацию и применяет изменения DOM напрямую.
<head>
<!--
DO: Use an inline module script when the experiment logic is small.
Module scripts are deferred by default (non-parser-blocking),
and blocking="render" ensures rendering waits for execution.
-->
<script type="module" blocking="render">
// Fetch the experiment configuration from your testing platform.
const config = await fetch('/api/experiment?id=homepage-cta')
.then(res => res.json());
// Apply the variant by setting a data attribute on <html>.
// CSS rules keyed to this attribute will style the variant.
document.documentElement.dataset.variant = config.variant;
</script>
<style>
/* Default styles (control group) */
.cta-button {
background-color: blue;
}
/* Variant B styles, activated by the data attribute */
[data-variant="b"] .cta-button {
background-color: green;
}
</style>
</head>
Рекомендации
- ОБЯЗАТЕЛЬНО. Скрипт эксперимента должен выполняться быстро. Медленный скрипт задерживает отрисовку. Установите бюджет производительности (например, время выполнения менее 100 мс) для скрипта, блокирующего отрисовку.
- РЕКОМЕНДУЕТСЯ отделить логику эксперимента от скрипта, блокирующего отрисовку. Загрузите небольшой блокирующий рендеринг фрагмент, который применяет вариант, а затем загрузите более тяжелые скрипты отслеживания или аналитики отдельно с помощью
asyncилиdefer(безblocking="render"). - РЕКОМЕНДУЕТСЯ использовать атрибут данных в
<html>или<body>, чтобы указать активный вариант, и селекторы CSS, привязанные к этому атрибуту, для оформления варианта. Это позволяет избежать прямого манипулирования DOM в скрипте, блокирующем отрисовку. - НЕ используйте
blocking="render"в скриптах аналитики, отслеживания и других невидимых скриптах. Блокировать отрисовку должны только скрипты, которые меняют то, что пользователь видит при первой отрисовке. - НЕ используйте
blocking="render"вместе с устаревшими фрагментами, скрывающими страницу. Они решают одну и ту же проблему, и использование обоих приводит к ненужным задержкам.
Резервные стратегии
Атрибут blocking поддерживается не во всех браузерах. В браузерах, которые не поддерживают этот атрибут, он игнорируется, и скрипт загружается в режиме работы по умолчанию (async в примерах выше), что может привести к мерцанию. Резервный вариант необходим, чтобы предотвратить мерцание в неподдерживаемых браузерах.
Резервный вариант: фрагмент, скрывающий страницу
Используйте легкий фрагмент, скрывающий страницу, в качестве резервного варианта, только если blocking="render" не поддерживается. Проверьте поддержку функции и пропустите резервный вариант в браузерах, которые обрабатывают ее нативно.
<head>
<!--
DO: Load the experiment script with blocking="render" for
browsers that support it. This is the preferred approach.
-->
<script
src="https://cdn.example.com/experiment-sdk.js"
async
blocking="render"
></script>
<script>
// DO: Only apply the anti-flicker fallback in browsers
// that do not support blocking="render".
if (!Object.hasOwn(HTMLScriptElement.prototype, 'blocking')) {
// Hide the page until the experiment script runs.
document.documentElement.classList.add('ab-loading');
// DO: Set a timeout to reveal the page if the experiment
// script takes too long. This prevents an indefinitely
// blank page on slow connections. Adjust the timeout
// to match your experiment SDK's expected load time.
setTimeout(() => {
document.documentElement.classList.remove('ab-loading');
}, 4000);
}
</script>
<style>
/*
DO: Use opacity to hide content during experiment loading.
This is only applied when blocking="render" is unsupported.
*/
.ab-loading {
opacity: 0 !important;
}
</style>
</head>
// DO: In your experiment SDK's initialization callback,
// remove the fallback class to reveal the page.
function onExperimentReady() {
document.documentElement.classList.remove('ab-loading');
}
О чем ещё нужно помнить
- Влияние на производительность.
blocking="render"снижает скорость первой отрисовки, чтобы обеспечить визуальную корректность. Отслеживайте показатели Largest Contentful Paint (LCP) и First Contentful Paint (FCP), чтобы убедиться, что скрипт эксперимента не вызывает чрезмерной задержки. - Надежность сторонних скриптов. Если SDK эксперимента размещен в сторонней сети доставки контента, то сбой в ее работе может полностью заблокировать отрисовку. Браузер применяет собственную эвристику времени ожидания (которое может превышать четыре секунды), но разработчик не может управлять временем ожидания для
blocking="render". Убедитесь, что сторонний поставщик гарантирует высокую доступность. - Серверные альтернативы: Для страниц, где производительность критически важна, вместо клиентского тестирования можно использовать серверное A/B-тестирование (когда сервер сразу отрисовывает нужный вариант). Серверные подходы полностью устраняют мерцание без блокировки отрисовки. Используйте
blocking="render"на стороне клиента, только если тестирование на стороне сервера невозможно.