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

Начиная с версии 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
Поделиться публикацией
Ой, у вас баннер убежал!

Ну. И что?
Реклама
Комментарии 16
    +2
    Возможно, автор сей статьи сможет заодно пояснить, почему при нажатии кнопки «Назад» в Safari, если предыдущая страница была главной, часто перекидает на незалогиненную страницу :)? Разработчики Хабра добавили костыль, который перезагружает страницу, когда это происходит, но всё равно механизм этого бага очень интересен.
      0
      А можно пример?
        0
        У меня стабильно при нажатии кнопки «Назад» в Safari кидает на незалогиненную главную страницу :). И такое происходит только на Хабре.
      0
      В Using Firefox 1.5 caching написано — если страница использует unload, то кэширование не работает, вы немного уточняете это утверждение. Но все же не понятно в какой момент и как браузер определяет наличие обработчика unload, если как описано в документации, то как только вы что-то повесили на unload, то мы можем говорить, что мы уже используем unload даже если потом мы отцепили обработчик. Мне приходит в голову единственный момент, в который браузер проверяет наличие обработчика это после его исполнения unload, тогда почему нельзя его использовать, статья начиналась с фразы
      Разработчик может посчитать, что данное поведение ни что иное как баг браузера или какая-то его особенность, и в поисках быстрого решения бездумно вставить лишний обработчик на событие unload (первый пункт в списке, приведенном дальше). Тем самым разработчик отказывается от BFCache вообще, тем самым лишая своих пользователей возможности практически мгновенного перемещения по посещенным страницам.
        0
        Грубо говоря браузер после того как уйти со страницы выполнив все обработчики в том числе и unload, уже в своем окружении проверяет, а висит ли хоть один обработчик на unload, если его unload undefined, то тогда кэшировать, если остальные конечно условия в вашем списке тоже выполняются. Так ли это?
          0
          насколько я понимаю, именно по наличию unload хендлеров браузер и определяет, включать ли bfcache для данного ресурса или нет.
        0
        Вопрос — как можно принудительно отключить в Мозилле кэширование страниц, с которых ушел, а то уж больно безбожно жрёт память.

        P.S. Как, как может 5 страничек с обычными ненагруженными страничками пожирать 624 Мб памяти?
          +1
          Биткоины майнят. Mozilla ведь некоммерческая организация :-D
            +4
            Когда вы уходите со страницы — она «консервируется», т.е. выполнение её скриптов останавливается.
            +5
            Эх… вспоминаю теплое ламповое кэширование страниц в Opera < 9.20 (с версией могу ошибиться). Которое было включено всегда. И пусть скрипты ломались при этом, но на диалапе такое кэширование было то что надо.
              +5
              Opera… Сколько в слове этом. Вспомнилось как лет 5 назад мне нужно было получить расписание экзаменов. Мы привыкли что оно всё время доступно в сети и никуда не переписывали. Осталась пара дней до экзамена, а во сколько он и в какой аудитории никто не знает. Ну а сайт лежит мёртвым сном. Я вспомнил что посещал эту страницу ранее, около 2-3-ёх недель назад. Включил offline-режим, набрал нужный URL и страница «всплыла передо мной». Со всеми стилями и пр… Такой кеш надолго запомнился. А сейчас такое поведение, наверное, уже почти не встретишь. Повсюду груды JavaScript-а и прочих сложностей, сводящих подобный кеш на нет.
                +3
                Меня один раз так Firefox спас когда я вкладки перепутал и вместо главного шаблона сайта в админке вставил шаблон от компонента (сайт был на Битрикс и без какого-либо доступа кроме админки). Увидев как вместо сайта пустую страницу я сообразил что что-то не то и чуть было уже не отложил кирпичей. Но вовремя вспомнил про «Автономный режим». Пару кликов «Назад» и вот уже передо мной форма с оригинальным кодом шаблона. Отключил кэширование и ткнул по сабмиту в форме… Пронесло =)
              –1
              Когда пользователь возвращается на закешированную страницу, скрипты не выполняются заново

              Важно уточнить, что не выполняются только внешние скрипты, подключенные через script src="". Inline-скрипты выполняются всегда. Не берусь утверждать, но по-моему так будет более кроссбраузерно.
                0
                Скрипты уже выполнились. Если их выполнить повторно, то это может только привести к ошибкам, а пользы никакой не даст.
                  0
                  Польза будет та же что и от pageshow/pagehide. Ошибки выявляются на этапе тестирования.
                  Тем не менее факт остается фактом: inline-скрипты выполняются при переходе вперед/назад.
                    0
                    Не подтверждаю. Кликаю назад в firefox — inline скрипт не выполняется. Chromium выполняет оба. Т.е. перегружает страницу.

              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

              Самое читаемое