Тестирование поведения при bfcache

bfcache (back/forward cache) — это механизм браузера, который позволяет мгновенно восстанавливать состояние страницы при навигации «назад» или «вперед». Страница при этом не перезагружается полностью, а возвращается из кэша вместе со всеми скриптами, DOM и состоянием. Это ускоряет работу и повышает отзывчивость интерфейса, но накладывает особенности на измерения Web Vitals.


Влияние bfcache на метрики Web Vitals

Core Web Vitals (LCP, FID, CLS) изначально рассчитаны для обычной загрузки страницы. При использовании bfcache поведение этих метрик меняется:

  1. LCP (Largest Contentful Paint)

    • На восстановленной странице LCP может не пересчитываться автоматически, потому что контент уже отрендерен.
    • Для корректного измерения необходимо использовать событие pageshow с persisted = true, чтобы определить, что страница восстановлена из bfcache, и заново инициировать измерение LCP.
  2. FID (First Input Delay)

    • FID фиксируется при первой интеракции пользователя после загрузки страницы.
    • При возврате из bfcache браузер может не генерировать новое событие загрузки, поэтому FID может быть неполным.
    • Рекомендуется отслеживать события pointerdown, keydown и touchstart с учетом pageshow.persisted.
  3. CLS (Cumulative Layout Shift)

    • На странице из bfcache CLS может быть уже «заморожен».
    • При восстановлении состояния DOM может происходить дополнительное смещение элементов, если скрипты выполняются повторно.
    • Для корректного измерения CLS требуется создавать новый объект LayoutShift после события pageshow.

Практические рекомендации для измерений

import {getLCP, getFID, getCLS} from 'web-vitals';

function trackVitalsFromBFCache() {
  function initWebVitals() {
    getLCP(console.log);
    getFID(console.log);
    getCLS(console.log);
  }

  // Обработка обычной загрузки
  window.addEventListener('load', initWebVitals);

  // Обработка возврата из bfcache
  window.addEventListener('pageshow', (event) => {
    if (event.persisted) {
      initWebVitals();
    }
  });
}

trackVitalsFromBFCache();

Разбор кода:

  • Используется событие pageshow, которое срабатывает как при обычной загрузке, так и при восстановлении из bfcache.
  • Свойство persisted указывает на то, что страница была загружена из кэша, а не с сервера.
  • Вызов getLCP, getFID и getCLS повторно позволяет корректно собирать метрики после восстановления состояния.

Особенности поведения при навигации

  • Скрипты, которые привязаны к DOMContentLoaded, не срабатывают при восстановлении страницы из bfcache.
  • Таймеры, установленные через setTimeout или requestAnimationFrame, могут оставаться в состоянии «заморожено», пока страница не будет видима.
  • Анимации CSS и Web Animations API также могут быть приостановлены, что влияет на точность CLS и LCP.

Пример проверки CLS при восстановлении страницы:

let clsValue = 0;
let clsEntries = [];

const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    if (!entry.hadRecentInput) {
      clsValue += entry.value;
      clsEntries.push(entry);
    }
  }
});

observer.observe({type: 'layout-shift', buffered: true});

window.addEventListener('pageshow', (event) => {
  if (event.persisted) {
    console.log('CLS после восстановления из bfcache:', clsValue);
    // Очистка для следующего восстановления
    clsValue = 0;
    clsEntries = [];
  }
});

Тонкости мониторинга

  • Метрики Web Vitals должны собираться каждый раз при появлении страницы, независимо от способа загрузки.
  • Использование buffered: true при наблюдении за Layout Shift позволяет захватывать события, которые произошли до подключения наблюдателя.
  • Для аналитики и отчетности необходимо различать обычную загрузку и восстановление из bfcache, чтобы не искажать данные.

Важные моменты при интеграции

  • Метрики LCP, FID и CLS могут сильно отличаться при возврате пользователя на страницу, поэтому все аналитические библиотеки должны учитывать pageshow.persisted.
  • Если сайт активно использует SPA-фреймворки, нужно интегрировать Web Vitals с системой маршрутизации и событиями жизненного цикла компонентов.
  • Тестирование bfcache должно включать сценарии: быстрый переход «назад», множественные возвраты и повторные взаимодействия с интерактивными элементами.