Pull to refresh

Back/Forward Cache — механизм кеширования страниц в браузере

Reading time 4 min
Views 22K
Начиная с версии 1.5, в Firefox появился механизм кеширования, сохраняющий состояние страницы в памяти. Кеширование действует на одну сессию браузера. Перемещаясь по посещенным страницам с использованием кнопок «Назад/Вперёд», нет необходимости загружать страницу с сервера целиком. При этом вся страница, включая js-скрипты, как бы «консервируются» в том состоянии, в котором они были, когда пользователь их покидал. Данный механизм позволяет производить навигацию по посещенным страницам крайне быстро. Состояние кеша остается неизменным, пока действует сессия браузера (пока пользователь не закроет закладку, или браузер).

Не «баг», а «фича»

Помимо прочего, непонимание данного механизма браузеров вызывает настоящую головную боль у разработчиков. Рассмотрим пример.
Есть форма, данные которой мы хотим отправить на сервер. Мы хотим как-то визуализировать этот процесс и запускаем спиннер при отправке формы. Браузер переходит на следующую страницу. Если мы вернемся назад, используя кнопку браузера «Назад» (или через window.history.back()), то скорее всего увидим, что спиннер так и вращается, хотя на самом деле уже ничего не происходит.

Разработчик может посчитать, что данное поведение ни что иное как баг браузера или какая-то его особенность, и в поисках быстрого решения бездумно вставить лишний обработчик на событие unload (первый пункт в списке, приведенном дальше). Тем самым разработчик отказывается от BFCache вообще, тем самым лишая своих пользователей возможности практически мгновенного перемещения по посещенным страницам.

Условия работы

Механизм кеширования страниц не работает, если:
  • на странице определены обработчики для событий unload, beforeunload;
  • для страницы установлен Сache-control: no-store;
  • сайт находится под HTTPS и для страницы установлено хотя бы одно из следующих правил:
    • Cache-Control: no-cache
    • Pragma: no-cache
    • Expires: 0 или «Expires» указан в прошлом относительно заголовка «Date» (за исключением когда
    • "Cache-Control: max-age=" также указан)
  • страница не полностью загрузилась;
  • страница использует механизм транзакций IndexedDB;
  • страница верхнего уровня содержит frame, iframe (которые, к слову не кешируются никогда).


События pageshow, pagehide

С появлением BFCache, вместе с ним, появились два новых события. Чтобы приблизиться к ним поближе, рассмотрим стандартное поведение веб-страницы:
  1. Пользователь переходит на страницу.
  2. С загрузкой страницы выполняются js-скрипты.
  3. Как только страница загрузилась, возникает событие load.

Для некоторых страниц существует и 4 шаг. Если на странице используются обработчики для unload, beforeunload, то эти события вызываются браузером в момент, когда пользователь покидает страницу. В этом случае страница не закешируется.

Когда пользователь возвращается на закешированную страницу, скрипты не выполняются заново, и событие load также не возникает (шаги 2,3), т.к. в большинстве случаев эта работа не нужна, и поэтому состояние страницы остается прежним.

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

Событие pageshow

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

Событие pageshow содержит в себе булевое свойство persisted, которое равно false при первой загрузке страницы. Оно устанавливается в true, если страница закеширована браузером (т.е. если это не первая загрузка страницы).

Событие pagehide

Если необходимо определить поведение для момента, когда пользователь покидает страницу, но нет желания использовать unload событие (которое не даст странице закешироваться), следует использовать событие pagehide.

Как и pageshow, pagehide содержит булевое свойство persisted. Аналогично, оно имеет значение false если страница не закеширована, и true в обратном случае.
Если это свойство установлено в false, то обработчик unload выполняется незамедлительно после события pagehide.

Кеширование несмотря на unload и beforeunload

Если возникла ситуация, когда нужно использовать события unload, beforeunload, но при этом сохранить возможность BFCache, можно просто удалить эти события в их обработчике, и переназначить их в обработчике события pageshow:

window.addEventListener('pageshow', PageShowHandler, false);
 
window.addEventListener('unload', UnloadHandler, false);
 
function PageShowHandler() {
    window.addEventListener('unload', UnloadHandler, false);
}
 
function UnloadHandler() {
    window.removeEventListener('unload', UnloadHandler, false);
}


Кроссплатформенность

Механизм BFCache появился в Firefox 1.5, и уже давно активно поддерживается всеми современными браузерами. Для проверки поддержки браузерами, можно использовать следующий подход:

if ('onpagehide' in window) {
    window.addEventListener('pagehide', exitFunction, false);
} else {
    window.addEventListener('unload', exitFunction, false);
}


Ссылки по теме

Using Firefox 1.5 caching
Working with BFCache
Tags:
Hubs:
+41
Comments 16
Comments Comments 16

Articles