Оптимизируйте свои страницы для мгновенной загрузки при использовании кнопок браузера «Назад» и «Вперед».

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

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

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

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

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

основы bfcache

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

Сколько раз вы посещали веб-сайт и нажимали ссылку, чтобы перейти на другую страницу, только чтобы понять, что это не то, что вы хотели, и нажимали кнопку «Назад»? В этот момент bfcache может сильно повлиять на скорость загрузки предыдущей страницы:

Без включенного bfcache

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

С включенным bfcache

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

Посмотрите это видео о bfcache в действии, чтобы понять, какое ускорение он может дать навигации:

В видео выше пример с bfcache немного быстрее, чем пример без него.

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

Данные об использовании Chrome показывают, что 1 из 10 переходов на ПК и 1 из 5 на мобильных устройствах выполняются либо назад, либо вперед. С включенным bfcache браузеры могут исключить передачу данных и время, затрачиваемое на загрузку миллиардов веб-страниц каждый божий день!

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Никогда не используйте событие unload

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

Событие unload проблематично для браузеров, потому что оно предшествует bfcache, и многие страницы в Интернете работают в соответствии с (разумным) предположением, что страница не будет продолжать существовать после запуска события unload. Это представляет собой проблему, поскольку многие из этих страниц были также построены с предположением, что событие unload будет срабатывать всякий раз, когда пользователь уходит, что больше не соответствует действительности (и «не было верно для долгое время").

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

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

Поскольку 65% страниц в Chrome регистрируют прослушиватель событий unload, чтобы иметь возможность кэшировать как можно больше страниц, Chrome решил согласовать реализацию с Safari.

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

На самом деле, Lighthouse v6.2.0 добавил no-unload-listeners audit, который будет предупреждать разработчиков, если какой-либо JavaScript на их страницах (в том числе из сторонних библиотек) добавляет прослушиватель событий unload.

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

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

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

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

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

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

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

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

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

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

Однако, если эти задачи подключены к API, которые также доступны с других страниц того же источника (например, IndexedDB, Web Locks, WebSockets и т. д.), это может быть проблематично, поскольку приостановка этих задач может помешать запуску кода на других вкладках. .

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

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

Затем, если страница восстанавливается из bfcache, вы можете повторно открыть или повторно подключиться к этим API (в событии pageshow или resume).

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

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

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

В настоящее время в Chrome страница может оставаться в bfcache до трех минут, что должно быть достаточно для запуска теста (с использованием такого инструмента, как Puppeteer или WebDriver), чтобы убедиться, что свойство persisted события pageshow true после перехода со страницы и нажатия кнопки "Назад".

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

Способы отказаться от bfcache

Если вы не хотите, чтобы страница сохранялась в bfcache, вы можете убедиться, что она не кэшируется, установив заголовок Cache-Control в ответе страницы верхнего уровня на no-store:

Все остальные директивы кэширования (включая no-cache или даже no-store в подфрейме) не повлияют на право страницы на использование bfcache.

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

Кроме того, в Chrome в настоящее время возможен отказ на уровне пользователя с помощью флага #back-forward-cache, а также отказ на основе корпоративной политики.

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

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

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

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

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

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

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

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

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

Результатом является меньше быстрых загрузок страниц в вашем наборе данных, что, вероятно, замедлит распределение, несмотря на тот факт, что производительность, с которой сталкивается пользователь, вероятно, улучшилась!

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

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

Внимание! Тип навигации back_forward в Navigation Timing API не следует путать с восстановлением bfcache. Navigation Timing API аннотирует только загрузки страниц, тогда как восстановление bfcache повторно использует страницу, загруженную из предыдущей навигации.

Влияние на Core Web Vitals

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

Такие инструменты, как Отчет о пользовательском опыте Chrome, которые собирают и сообщают о показателях Core Web Vitals, скоро будут обновлены, чтобы рассматривать восстановления bfcache как отдельные посещения страниц в наборе данных.

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

  • Для наибольшей отрисовки содержимого (LCP) вы можете использовать дельту между временной меткой событияpageshow и временной меткой следующего окрашенного кадра (поскольку все элементы в кадре будут окрашены одновременно). Обратите внимание, что в случае восстановления bfcache LCP и FCP будут одинаковыми.
  • Для задержки первого ввода (FID) вы можете повторно добавить прослушиватели событий (те же, что используются FID polyfill) в событииpageshow и указать FID как задержку первого ввода после восстановления bfcache.
  • Для Cumulative Layout Shift (CLS) вы можете продолжать использовать существующий Performance Observer; все, что вам нужно сделать, это сбросить текущее значение CLS до 0.

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

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

Первоначально опубликовано на https://web.dev.