Назад к блогу

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

API Speculation Rules в Chrome может отрендерить страницу ещё до того, как пользователь щёлкнет по ссылке. Вот как это искажает наивную аналитику — и как измерение без cookie на границе сети остаётся честным.

Страница, которая загрузилась до клика

API Speculation Rules, теперь широко доступный в браузерах на базе Chromium, позволяет сайту объявлять, какие URL браузер должен предварительно загрузить или полностью отрендерить ещё до навигации. Когда пользователь наводит курсор или проявляет намерение, страница назначения может быть уже отрендерена в скрытой вкладке, а затем мгновенно подставлена. Выигрыш — практически нулевой Largest Contentful Paint при активации. Проблема же, с точки зрения измерений, в том, что предварительно отрендеренная страница выполняет ваши скрипты ещё до того, как кто-либо решил её посетить.

Если ваша аналитика фиксирует просмотр страницы при загрузке, предварительный рендеринг завышает ваши показатели визитами, которых никогда не было. Хуже того, это искажает данные о производительности: страница, отрендеренная в фоне, записывает тайминги, которые никак не соотносятся с тем, что пережил реальный пользователь.

Что на самом деле происходит при предварительном рендеринге

Предварительно отрендеренный документ выполняется как обычно, но в особом состоянии жизненного цикла. document.prerendering равен true, и страница ещё не активирована в видимой вкладке. Событие prerenderingchange срабатывает тогда — и только тогда, — когда пользователь действительно переходит на неё. Многие предварительные рендеры вообще не активируются; браузер может их отбросить.

Это важно по двум причинам:

  1. Подсчёт. Наивный sendBeacon на DOMContentLoaded сообщит о просмотре страницы для документа, который, возможно, никогда не будет увиден.
  2. Атрибуция производительности. Core Web Vitals определяются относительно воспринимаемой пользователем загрузки. Библиотека web-vitals и лежащие в её основе спецификации Paint Timing и Event Timing учитывают предварительный рендеринг, измеряя от точки активации, а не от начала исходной навигации. Метрика, собранная без этого понимания, попросту неверна.

Правильный подход

Веб-платформа даёт вам чистые сигналы. Откладывайте любое измерение до момента, когда страница действительно станет видна человеку:

if (document.prerendering) {
  document.addEventListener('prerenderingchange', recordPageview, { once: true });
} else {
  recordPageview();
}

Для таймингов PerformanceNavigationTiming.activationStart даёт смещение между началом навигации и активацией. Любую метку времени отрисовки или взаимодействия следует измерять относительно этого значения, обрезая отрицательные значения до нуля. Именно это внутри делает библиотека web-vitals от Google, так что, если вы вычисляете LCP или INP самостоятельно, воспроизведите эту логику.

Та же дисциплина применима к prefetch, более лёгкому родственнику предварительного рендеринга. Prefetch лишь загружает ответ; он не выполняет скрипты, поэтому редко напрямую загрязняет аналитику. Но он потребляет трафик и может искажать серверные журналы запросов — напоминание о том, что сырой подсчёт по журналам и реальные визиты расходятся сильнее, чем когда-либо.

Почему без cookie и клиентского состояния это проще

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

Monoid обходит весь этот класс проблем. Мы ничего не храним на устройстве — ни cookie, ни localStorage, ни отпечатка. Нет спекулятивной сессии, которую нужно создать, а затем отменить. Измерение — это единый сигнал без состояния, который мы отправляем только тогда, когда страница становится реальной. Поскольку сбор данных выполняется на границе сети Cloudflare, агрегация происходит на данных, которые уже отражают подлинные, активированные навигации, а не оптимистичные догадки браузера.

Это минимизация данных, работающая вам на пользу. Когда вы не собираете ничего, что потом нужно вычищать, спекулятивный рендеринг не создаёт осиротевших записей идентичности или дублирующихся профилей. Это просто означает, что перед подсчётом вы дожидаетесь prerenderingchange.

Чек-лист аналитики, безопасной для предварительного рендеринга

  • Ставьте отправку просмотра страницы в зависимость от document.prerendering и события prerenderingchange.
  • Вычисляйте LCP, FCP и INP относительно activationStart или используйте библиотеку, которая уже это делает.
  • Рассматривайте серверные журналы запросов как надмножество реальных визитов, а не как их приближение.
  • Убедитесь, что ваша Content-Security-Policy позволяет достичь вашей конечной точки измерений из активированного документа; проверьте это в панели предварительного рендеринга Chrome DevTools.
  • Избегайте любых спекулятивных записей в хранилище устройства — самый чистый способ быть корректным относительно предварительного рендеринга — вообще не хранить клиентское состояние.

Более широкий смысл

Каждый новый примитив производительности — bfcache, мягкие навигации, а теперь и правила спекулятивной загрузки — незаметно нарушает старое предположение, что загрузка страницы равна визиту человека. Аналитике, опирающейся на cookie и сессии с состоянием, приходится нагромождать всё больше логики согласования. Аналитике, которая измеряет событие без состояния в момент подлинной видимости, достаточно уважать события жизненного цикла платформы. Быстрые страницы и честные цифры не противоречат друг другу; нужно лишь считать в правильный момент.

Sources

Comments

Loading comments…