Возврат страницы из back-forward cache
В прошлом уроке мы отделили способ перехода от состояния доставки. Теперь рассмотрим случай, когда кнопка Back возвращает уже подготовленный документ. Такой механизм называется back-forward cache, или bfcache. Его польза связана с сохранением состояния страницы, поэтому проверять нужно не только сетевые запросы, но и поведение интерфейса после восстановления.
Продолжаем работать с advanced/history-lab. Наблюдение выполняет читатель вручную; автор не запускал стенд и не получил измерение восстановления. Цель урока — определить, сохранился ли документ, объяснить состояние программы и составить договор её возобновления. Гарантированного попадания каждой страницы в bfcache пример не обещает.
Сохранённый документ и сохранённый ответ
HTTP-кеш хранит ответы ресурсов. При новом документе браузеру всё равно требуется обработать разметку и исполнить нужную программу. Bfcache сохраняет состояние страницы, включая память JavaScript. Возвращение может продолжить прежний документ вместо повторного создания. Это принципиальная граница, описанная в руководстве web.dev по bfcache.
Для статьи полезно сохранить место чтения и введённую учебную заметку. Но сама сохранность поля не доказывает механизм: браузер способен восстанавливать некоторые значения формы и при другом пути истории. Поэтому в стенде есть отдельный журнал событий. Мы связываем видимый результат с pageshow.persisted, а не пытаемся угадать причину по одному признаку.
Откройте first.html, введите произвольное учебное слово, затем перейдите к second.html. Вернитесь кнопкой Back. Смотрите на последнюю строку журнала первой страницы и значение boot. Если persisted истинно, восстановился документ. Если ложно, исследуйте новый путь загрузки. Оба результата допустимы для разных окружений.
События входа и выхода
В исходниках используется простой обработчик. Это полный принцип подключения журнала; элементы уже находятся в HTML стенда:
const boot = Date.now();
const events = document.querySelector("#events");
function log(event) {
events.value += `${event.type}: persisted=${event.persisted}; boot=${boot}\n`;
}
window.addEventListener("pageshow", log);
window.addEventListener("pagehide", log);
При начальном показе также приходит pageshow, поэтому наличие самого события не означает восстановление. Значение persisted уточняет, относится ли переход к сохранённому документу. Назначение свойства приведено в MDN: PageTransitionEvent.persisted.
У pagehide истинное значение показывает намерение браузера сохранить страницу, но не гарантирует будущего успешного возврата. Документ может позднее перестать быть доступным для восстановления. Поэтому окончательный вывод делается по последующему pageshow, а не по обещанию, которое якобы содержалось в событии ухода.
Журнал не сохраняется в cookies или storage. Если программа создаётся заново, её локальное состояние тоже создаётся заново. В случае bfcache прежние переменные продолжают существовать. Значение boot помогает объяснить эту связь: одинаковая инициализация относится к сохранённому экземпляру программы, а не к автоматически обновлённой копии ресурса.
Повторная инициализация может испортить результат
Представим, что на каждом pageshow разработчик заново подключает обработчик кнопки и создаёт карточки. После нескольких возвратов один клик способен вызвать несколько действий. Появятся лишние элементы или повторная работа. Причина не в медленном сохранении браузера, а в отсутствии различия между первоначальным запуском и восстановлением.
Основную инициализацию делайте один раз для нового документа. В обработчике восстановления выполняйте только необходимую проверку состояния. У статического учебного текста может вообще не быть обязательного обновления. Полная перезагрузка ради привычной последовательности уничтожит полезное сохранение места чтения и превратит возврат в новый сетевой сценарий.
В простом стенде обработчики журнала подключены модулем один раз. pageshow только добавляет строку. Он не создаёт второе поле и не сбрасывает заметку. Ожидаемое поведение выводится из структуры исходников, но фактическую последовательность событий и сохранность состояния нужно сверить в браузере самостоятельно.
У сложного приложения есть другие требования. Например, данные наличия товара могли измениться на второй странице. Тогда нужно определить, какая часть требует обновления после возврата и как сохранить остальную композицию. Стабильная учебная статья и живой административный интерфейс имеют разные договоры свежести; единый безусловный reload плохо выражает это различие.
Пригодность и причина отсутствия восстановления
Bfcache — решение браузера, а не хранилище с обещанным сроком для приложения. Ограничения памяти, состояние документа и поведение API могут повлиять на доступность. Если один возврат восстановился, это не гарантирует все следующие. Поэтому протокол сохраняет браузер, сценарий, паузу и фактическое событие.
В Chrome DevTools есть область Application → Back-forward Cache. Она позволяет читателю отдельно исследовать пригодность документа и причины отказа. Такое действие изменяет последовательность переходов, поэтому его результат следует хранить отдельно от обычного ручного Back. Устройство инструмента описано в документации Chrome DevTools.
Не добавляйте unload ради сохранения состояния страницы. Старые схемы ухода плохо соответствуют возможности возвращения сохранённого документа. Здесь используются события перехода страницы. Если в существующем приложении есть обработчики ухода, сначала выясните, какую функцию они обеспечивают, и предусмотрите её правильный жизненный цикл. Удаление нужной функции ради успешного индикатора инструмента не является завершённым решением.
Отдельно проверяйте актуальные правила заголовков. Нельзя утверждать, что Cache-Control: no-store во всех браузерах всегда исключает bfcache. Chrome изменил такое поведение при определённых безопасных условиях; другие браузеры и состояния могут отличаться. Это описано в документации Chrome о no-store и bfcache. Правила HTTP-кеша и сохранения документа нужно исследовать раздельно.
Что делать с продолжаемой работой
Если программа поддерживает соединение или длительную внешнюю операцию, уход страницы требует отдельного договора. Возможное сохранение не должно блокировать другие документы или создавать второе соединение после возврата. В нашем стенде таких операций нет: он специально ограничен локальной памятью и обычными элементами.
Для будущего расширения обозначьте состояния «активен», «приостановлен» и «возобновлён». Затем определите одну точку владения операцией. Если возобновление вызывается несколькими событиями, функция должна избегать повторного подключения. Сохранённый документ продолжает иметь прежнюю память, поэтому предположение «всё начинается с нуля» здесь неверно.
Также не используйте время старого Navigation Timing как задержку возврата. При восстановлении человек получает другой пользовательский этап. Для настоящих метрик нужен сборщик, учитывающий это состояние; учебный наблюдатель из урока 13 его намеренно не рассчитывает. Отсутствие новой строки LCP-кандидата не означает бесконечную задержку и не позволяет подставить старое число.
Сохраним функциональную проверку
Проверьте не только последнее событие, но и то, что читатель продолжает делать после него. Введённое слово остаётся доступным, управление работает один раз, ссылка ведёт к нужному документу. Если программу дополняли, повторите несколько циклов истории и сравните количество обработчиков через их видимый эффект. Это будущая ручная проверка, а не выполненный автором тест.
Запишите итог: восстановился ли документ, что сохранилось и какую работу приложение повторило. Если bfcache недоступен, сохраните причину или состояние неизвестности. Такая запись позволяет обсуждать возврат предметно. Следующий урок займётся другим механизмом раннего результата: подсказками браузеру о ресурсах, которые должны быть обоснованы конкретной задержанной фазой.