Обратный и прямой кеш

Кэш назад/вперед (или bfcache) — это оптимизация браузера, которая обеспечивает мгновенную навигацию вперед и назад. Это значительно улучшает возможности просмотра, особенно для пользователей с медленными сетями или устройствами.

На этой странице описано, как оптимизировать ваши страницы для bfcache во всех браузерах.

Совместимость с браузером

bfcache уже много лет поддерживается как в Firefox , так и в Safari , как на настольных компьютерах, так и на мобильных устройствах.

Начиная с версии 86, Chrome включил bfcache для навигации между сайтами на Android для небольшого процента пользователей. В последующих выпусках постепенно развертывалась дополнительная поддержка. Начиная с версии 96, bfcache включен для всех пользователей Chrome на настольных компьютерах и мобильных устройствах.

основы bfcache

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

В следующем видео показано, насколько bfcache может ускорить навигацию:

Использование bfcache позволяет страницам загружаться намного быстрее при навигации вперед и назад.

Данные об использовании Chrome показывают, что 1 из 10 переходов на компьютере и 1 из 5 на мобильных устройствах осуществляется либо вперед, либо назад. Благодаря этому bfcache может сэкономить много времени и данных.

Как работает «кэш»

«Кэш», используемый bfcache, отличается от кэша HTTP , который играет свою роль в ускорении повторной навигации. bfcache — это снимок всей страницы в памяти, включая кучу JavaScript, тогда как HTTP-кеш содержит только ответы на ранее сделанные запросы. Поскольку очень редко все запросы, необходимые для загрузки страницы, могут быть выполнены из HTTP-кеша, повторные посещения с использованием восстановления bfcache всегда выполняются быстрее, чем даже самая оптимизированная навигация без bfcache.

Однако создание снимка страницы в памяти сопряжено с некоторыми сложностями с точки зрения наилучшего сохранения выполняемого кода. Например, как обрабатывать вызовы setTimeout() , когда таймаут истекает, пока страница находится в bfcache?

Ответ заключается в том, что браузеры приостанавливают все ожидающие таймеры или невыполненные обещания для страниц в bfcache, включая почти все ожидающие задачи в очередях задач JavaScript , и возобновляют обработку задач, если страница восстанавливается из bfcache.

В некоторых случаях, например, для тайм-аутов и обещаний, это довольно низкий риск, но в других случаях это может привести к запутанному или неожиданному поведению. Например, если браузер приостанавливает задачу, необходимую для транзакции IndexedDB , это может повлиять на другие открытые вкладки в том же источнике, поскольку к одним и тем же базам данных IndexedDB могут быть доступны несколько вкладок одновременно. В результате браузеры обычно не пытаются кэшировать страницы в середине транзакции IndexedDB или при использовании API, которые могут повлиять на другие страницы.

Дополнительные сведения о том, как использование API влияет на соответствие страницы требованиям bfcache, см. в разделе Оптимизация страниц для bfcache .

Bfcache и iframe

Если страница содержит встроенные iframe, то сами iframe не подлежат использованию в bfcache. Например, если вы перейдете на другую страницу внутри iframe, а затем вернетесь назад, браузер вернется «назад» внутри iframe, а не в основной фрейм, но обратная навигация внутри iframe не будет использовать bfcache.

Основной фрейм также может быть заблокирован от использования bfcache, если встроенный iframe использует API, которые блокируют это. Чтобы избежать этого, можно использовать политику разрешений , установленную на основном фрейме, или использование атрибутов sandbox .

Bfcache и одностраничные приложения (SPA)

Поскольку bfcache работает с навигацией, управляемой браузером, он не работает для «мягкой навигации» в одностраничном приложении (SPA). Тем не менее, bfcache все равно может помочь при выходе из SPA и возвращении в него.

API для наблюдения за bfcache

Хотя bfcache — это оптимизация, которую браузеры выполняют автоматически, разработчикам все равно важно знать, когда это происходит, чтобы они могли оптимизировать свои страницы для нее и соответствующим образом корректировать любые показатели или показатели производительности .

Основными событиями, используемыми для наблюдения за bfcache, являются события перехода страниц pageshow и pagehide , которые поддерживаются большинством браузеров .

Новые события жизненного цикла страницы , freeze и resume , также отправляются, когда страницы входят в bfcache или покидают его, а также в некоторых других ситуациях, например, когда фоновая вкладка зависает, чтобы минимизировать загрузку ЦП. Эти события поддерживаются только в браузерах на базе Chromium.

Наблюдайте за восстановлением страницы из bfcache

Событие pageshow срабатывает сразу после события load при первоначальной загрузке страницы и каждый раз, когда страница восстанавливается из bfcache. Событие pageshow имеет persisted свойство, которое имеет true , если страница была восстановлена ​​из bfcache, и false в противном случае. Вы можете использовать свойство persisted , чтобы отличать регулярные загрузки страниц от восстановления bfcache. Например:

window.addEventListener('pageshow', (event) => {
  if (event.persisted) {
    console.log('This page was restored from the bfcache.');
  } else {
    console.log('This page was loaded normally.');
  }
});

В браузерах, поддерживающих API жизненного цикла страницы, событие resume срабатывает, когда страницы восстанавливаются из bfcache (непосредственно перед событием pageshow ) и когда пользователь повторно посещает замороженную фоновую вкладку. Если вы хотите обновить состояние страницы после ее заморозки (включая страницы в bfcache), вы можете использовать событие resume , но если вы хотите измерить частоту попаданий в bfcache вашего сайта, вам нужно будет использовать событие pageshow . В некоторых случаях вам может потребоваться использовать оба.

Подробные сведения о передовых методах измерения bfcache см. в разделе «Как bfcache влияет на аналитику и измерение производительности» .

Наблюдайте, когда страница входит в bfcache

Событие pagehide возникает либо при выгрузке страницы, либо когда браузер пытается поместить ее в bfcache.

Событие pagehide также имеет persisted свойство. Если это false , вы можете быть уверены, что эта страница не собирается войти в bfcache. Однако persisted true не гарантирует, что страница будет кэширована. Это означает, что браузер намерен кэшировать страницу, но могут быть и другие факторы, которые делают кэширование невозможным.

window.addEventListener('pagehide', (event) => {
  if (event.persisted) {
    console.log('This page *might* be entering the bfcache.');
  } else {
    console.log('This page will unload normally and be discarded.');
  }
});

Аналогично, событие freeze срабатывает сразу после события pagehide , если persisted имеет значение true , но это означает лишь то, что браузер намеревается кэшировать страницу. Возможно, от него все равно придется отказаться по ряду причин, которые будут объяснены позже.

Оптимизируйте свои страницы для bfcache

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

Используйте pagehide вместо unload

Самый важный способ оптимизировать bfcache во всех браузерах — никогда не использовать прослушиватели событий unload . Вместо этого слушайте pagehide , поскольку он срабатывает как при входе страницы в bfcache, так и при каждом запуске unload .

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

На настольных компьютерах Chrome и Firefox делают страницы с прослушивателями unload неприемлемыми для bfcache, что снижает риск, но также приводит к тому, что многие страницы не кэшируются и, следовательно, перезагружаются намного медленнее. Safari пытается кэшировать некоторые страницы с помощью прослушивателей событий unload , но, чтобы уменьшить вероятность поломки, он не запускает событие unload , когда пользователь уходит, что делает прослушиватели unload ненадежными.

На мобильных устройствах Chrome и Safari пытаются кэшировать страницы с помощью прослушивателей событий unload , поскольку ненадежность unload на мобильных устройствах снижает риск поломки. Мобильный Firefox рассматривает страницы, использующие unload , как неподходящие для bfcache, за исключением iOS, которая требует, чтобы все браузеры использовали механизм рендеринга WebKit, поэтому он ведет себя как Safari.

Чтобы определить, использует ли какой-либо JavaScript на ваших страницах unload , мы рекомендуем использовать аудит no-unload-listeners в Lighthouse .

Информацию о планах Chrome по прекращению поддержки события unload см. в разделе Прекращение поддержки события unload .

Используйте политику разрешений, чтобы предотвратить использование обработчиков выгрузки на странице.

Некоторые сторонние скрипты и расширения могут добавлять на страницу обработчики выгрузки, замедляя работу сайта и делая его недоступным для bfcache. Чтобы предотвратить это в Chrome 115 и более поздних версиях, используйте политику разрешений .

Permission-Policy: unload()

Добавляйте прослушиватели beforeunload только условно

Событие beforeunload не делает ваши страницы неприемлемыми для bfcache. Однако он ненадежен, поэтому мы рекомендуем использовать его только в случае крайней необходимости.

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

function beforeUnloadListener(event) {
  event.preventDefault();
  return event.returnValue = 'Are you sure you want to exit?';
};

// A function that invokes a callback when the page has unsaved changes.
onPageHasUnsavedChanges(() => {
  window.addEventListener('beforeunload', beforeUnloadListener);
});

// A function that invokes a callback when the page's unsaved changes are resolved.
onAllChangesSaved(() => {
  window.removeEventListener('beforeunload', beforeUnloadListener);
});

Сведите к минимуму использование Cache-Control: no-store

Cache-Control: no-store — это HTTP-заголовок, который веб-серверы могут устанавливать для ответов, который указывает браузеру не сохранять ответ в каком-либо HTTP-кеше. Он используется для ресурсов, содержащих конфиденциальную информацию пользователя, например страниц после входа в систему.

Хотя bfcache не является HTTP-кешем, браузеры исторически исключали страницы из bfcache, когда Cache-Control: no-store установлен для ресурса страницы (но не для какого-либо подресурса). Chrome работает над изменением этого поведения, сохраняя при этом конфиденциальность пользователей, но по умолчанию страницы, использующие Cache-Control: no-store не поддерживают bfcache.

Чтобы оптимизировать bfcache, используйте Cache-Control: no-store только на страницах, содержащих конфиденциальную информацию, которая не должна кэшироваться.

Для страниц, которые хотят всегда предоставлять актуальное содержимое, но не содержат конфиденциальную информацию, используйте Cache-Control: no-cache или Cache-Control: max-age=0 . Они сообщают браузеру о необходимости повторной проверки содержимого перед его обслуживанием и не влияют на соответствие страницы критериям bfcache, поскольку восстановление страницы из bfcache не задействует HTTP-кеш.

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

Обновите устаревшие или конфиденциальные данные после восстановления bfcache.

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

Например, если пользователь выходит из сайта на общедоступном компьютере, а следующий пользователь нажимает кнопку «Назад», устаревшие данные из bfcache могут включать личные данные, которые первый пользователь ожидал очистить при выходе из системы.

Чтобы избежать подобных ситуаций, всегда обновляйте страницу после события pageshow , если event.persisted имеет true :

window.addEventListener('pageshow', (event) => {
  if (event.persisted) {
    // Do any checks and updates to the page
  }
});

Для некоторых изменений вы можете вместо этого принудительно выполнить полную перезагрузку, сохранив историю навигации для навигации вперед. Следующий код проверяет наличие файла cookie, специфичного для сайта, в событии pageshow и перезагружается, если файл cookie не найден:

window.addEventListener('pageshow', (event) => {
  if (event.persisted && !document.cookie.match(/my-cookie)) {
    // Force a reload if the user has logged out.
    location.reload();
  }
});

Восстановление рекламы и bfcache

Может возникнуть соблазн попытаться избежать использования bfcache, чтобы ваша страница могла показывать новый набор объявлений при каждой навигации вперед или назад. Однако это плохо сказывается на производительности сайта и не приводит к постоянному увеличению вовлеченности рекламы. Например, пользователь может намереваться вернуться на страницу, чтобы щелкнуть рекламу, но если страница перезагружается, а не восстанавливается из bfcache, на ней может отображаться другое объявление. Мы рекомендуем использовать A/B-тестирование, чтобы определить лучшую стратегию для вашей страницы.

Для сайтов, которые хотят обновить объявления при восстановлении bfcache, вы можете обновить только объявления в событии pageshow , если event.persisted имеет значение true , не влияя на производительность страницы, как в этом примере тега публикации Google . Дополнительную информацию о рекомендациях для вашего сайта можно получить у своего поставщика объявлений.

Избегайте ссылок window.opener

В старых браузерах, если страница была открыта с помощью window.open() по ссылке с target=_blank без указания rel="noopener" , открывающаяся страница будет иметь ссылку на объект окна открытой страницы.

Помимо угрозы безопасности , страница с ненулевой ссылкой window.opener не может быть безопасно помещена в bfcache, поскольку она может привести к поломке любых страниц, которые попытаются получить к ней доступ.

Чтобы избежать этих рисков, используйте rel="noopener" для предотвращения создания ссылок window.opener . Это поведение по умолчанию во всех современных браузерах. Если вашему сайту необходимо открыть окно и управлять им с помощью window.postMessage() или напрямую ссылаясь на объект окна, ни открытое окно, ни средство открытия не подходят для bfcache.

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

Как упоминалось ранее, когда страница помещается в bfcache, он приостанавливает все запланированные задачи JavaScript и возобновляет их, когда страница извлекается из кеша.

Если эти запланированные задачи JavaScript получают доступ только к API DOM или другим API, изолированным от текущей страницы, приостановка этих задач, пока страница не видна пользователю, не вызывает проблем.

Однако если эти задачи связаны с API-интерфейсами, которые также доступны с других страниц того же источника (например: IndexedDB, Web Locks и WebSockets), их приостановка может привести к поломке этих страниц, предотвращая запуск кода на этих страницах.

В результате некоторые браузеры не будут пытаться поместить страницу в bfcache, если она имеет одно из следующих свойств:

Если ваша страница использует какой-либо из этих API, мы настоятельно рекомендуем закрывать соединения и удалять или отключать наблюдателей во время события скрытия или freeze pagehide . Это позволяет браузеру безопасно кэшировать страницу без риска повлиять на другие открытые вкладки. Затем, если страница восстановлена ​​из bfcache, вы можете повторно открыть или повторно подключиться к этим API во время события pageshow или resume .

В следующем примере показано, как убедиться, что страницы, использующие IndexedDB, имеют право на использование bfcache, закрыв открытое соединение в прослушивателе событий pagehide :

let dbPromise;
function openDB() {
  if (!dbPromise) {
    dbPromise = new Promise((resolve, reject) => {
      const req = indexedDB.open('my-db', 1);
      req.onupgradeneeded = () => req.result.createObjectStore('keyval');
      req.onerror = () => reject(req.error);
      req.onsuccess = () => resolve(req.result);
    });
  }
  return dbPromise;
}

// Close the connection to the database when the user leaves.
window.addEventListener('pagehide', () => {
  if (dbPromise) {
    dbPromise.then(db => db.close());
    dbPromise = null;
  }
});

// Open the connection when the page is loaded or restored from bfcache.
window.addEventListener('pageshow', () => openDB());

Протестируйте, чтобы убедиться, что ваши страницы кэшируются.

Инструменты разработчика Chrome помогут вам протестировать ваши страницы, чтобы убедиться, что они оптимизированы для bfcache, и выявить любые проблемы, которые могут помешать им участвовать в программе.

Чтобы протестировать страницу:

  1. Перейдите на страницу в Chrome.
  2. В DevTools выберите «Приложение» > «Обратный кеш» .
  3. Нажмите кнопку «Выполнить тест» . Затем DevTools пытается перейти назад и назад, чтобы определить, можно ли восстановить страницу из bfcache.
Панель обратного кэша в DevTools
Панель обратного кэширования в DevTools.

Если проверка прошла успешно, на панели появится сообщение «Восстановлено из обратного кэша». Если попытка не удалась, на панели будет указана причина.

Если причина связана с тем, что вы можете устранить как разработчик, панель помечает ее как «Действующая» .

DevTools сообщает о невозможности восстановить страницу из bfcache
Неудачный тест bfcache с действенным результатом.

На этом изображении использование прослушивателя событий unload делает страницу неприемлемой для bfcache. Вы можете это исправить, переключившись с unload на использование pagehide :

Делать
window.addEventListener('pagehide', ...);
Не
window.addEventListener('unload', ...);

В Lighthouse 10.0 также добавлен аудит bfcache , который выполняет аналогичный тест. Дополнительную информацию см . в документации аудита bfcache .

Как bfcache влияет на аналитику и измерение производительности

Если вы используете инструмент аналитики для отслеживания посещений вашего сайта, вы можете заметить уменьшение общего количества просмотров страниц, поскольку Chrome включает bfcache для большего количества пользователей.

Фактически, вы, вероятно, уже занижаете количество просмотров страниц в других браузерах, реализующих bfcache, поскольку большинство популярных аналитических библиотек не отслеживают восстановление bfcache как новые просмотры страниц.

Чтобы включить восстановление bfcache в счетчик просмотров страниц, установите прослушиватели для события pageshow и проверьте свойство persisted .

В следующем примере показано, как это сделать с помощью Google Analytics. Другие аналитические инструменты, вероятно, используют аналогичную логику:

// Send a pageview when the page is first loaded.
gtag('event', 'page_view');

window.addEventListener('pageshow', (event) => {
  // Send another pageview if the page is restored from bfcache.
  if (event.persisted) {
    gtag('event', 'page_view');
  }
});

Измерьте коэффициент попадания в bfcache

Чтобы определить страницы, которые еще не используют bfcache, измерьте тип навигации для загрузки страниц следующим образом:

// Send a navigation_type when the page is first loaded.
gtag('event', 'page_view', {
   'navigation_type': performance.getEntriesByType('navigation')[0].type;
});

window.addEventListener('pageshow', (event) => {
  if (event.persisted) {
    // Send another pageview if the page is restored from bfcache.
    gtag('event', 'page_view', {
      'navigation_type': 'back_forward_cache';
    });
  }
});

Рассчитайте коэффициент попадания в bfcache, используя значения для навигации back_forward и навигации back_forward_cache .

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

  • Закрытие и перезапуск браузера.
  • Дублирование вкладки.
  • Закрытие и восстановление вкладки.

В некоторых из этих случаев браузер может сохранить исходный тип навигации и отобразить тип back_forward , несмотря на то, что это не навигация назад или вперед. Даже если типы навигации сообщаются правильно, bfcache периодически удаляется для экономии памяти.

Из-за этого владельцы веб-сайтов не могут рассчитывать на 100% попадание в bfcache для всех переходов back_forward . Однако измерение их соотношения может помочь выявить страницы, которые препятствуют использованию bfcache.

Команда Chrome добавила API NotRestoredReasons , который помогает выявить причины, по которым страницы не используют bfcache, чтобы разработчики могли улучшить показатели попадания в bfcache.

Измерение производительности

bfcache также может отрицательно влиять на показатели производительности, собираемые в поле , в частности на показатели, измеряющие время загрузки страницы.

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

Есть несколько способов справиться с этой проблемой. Один из них — аннотировать все показатели загрузки страницы с указанием соответствующего типа навигации : navigate , reload , back_forward или prerender . Это позволяет вам продолжать отслеживать свою эффективность в этих типах навигации, даже если общее распределение имеет отрицательный результат. Мы рекомендуем этот подход для показателей загрузки страницы, не ориентированных на пользователя, таких как время до первого байта (TTFB) .

Для показателей, ориентированных на пользователя, таких как Core Web Vitals , лучшим вариантом будет указать значение, которое более точно отражает то, что испытывает пользователь.

Влияние на основные веб-показатели

Core Web Vitals измеряет взаимодействие пользователя с веб-страницей по различным параметрам (скорость загрузки, интерактивность, визуальная стабильность). Важно, чтобы ваши показатели Core Web Vitals отражали тот факт, что пользователи воспринимают восстановление bfcache как более быструю навигацию, чем загрузка страниц по умолчанию.

Инструменты, которые собирают и составляют отчеты по метрикам Core Web Vitals, такие как отчет об опыте пользователя Chrome , рассматривают восстановление bfcache как отдельные посещения страниц в своем наборе данных. И хотя не существует специальных API-интерфейсов веб-производительности для измерения этих показателей после восстановления bfcache, вы можете приблизительно оценить их значения, используя существующие веб-API:

Дополнительные сведения о том, как bfcache влияет на каждую метрику, см. на страницах руководств по отдельным метрикам Core Web Vitals. Конкретный пример реализации bfcache-версий этих метрик можно найти в PR-заявке о добавлении их в JS-библиотеку web-vitals .

Дополнительные ресурсы