31.82M

BFCache. Как мгновенно загрузить предыдущую страницу. Илья Сидорчик

1.

BFCache.
Как мгновенно
загрузить
предыдущую
страницу
Илья Сидорчик Разработчик
интерфейсов

2.

окно со спикером
Содержание
чтобы знать куда не заходить, но не забудь
удалить

3.

Название гла
ы

4.

Название гла
ы

5.

Перелистывание
приложения

6.

Перелис
тывание
книги

7.

Пример: Маркет—
Вайлдбериз.
Подпись: Работает

8.

BFCache работает между сайтами —
пригодится, даже если у вас SPA

9.

Статистика Хрома
по навигации
Каждый
10-й переход
Каждый
5-й переход
на десктопе
на таче
web.dev/bfcache

10.

BFCache
1
2
Ускоряет сервис
Улучшает UX
3
4
Экономит железо
и деньги
Помогает
экологии :-)

11.

BFCache — это новая
экспериментальная фича?

12.

BFCache — это стандартная
функциональность браузера

13.

Что такое
BFCache
Определение, история, поддержка юзер-агентами

14.

Определен
ие
BFCache
Back/Forward Cache (сокр. BFCache) — это браузерная
оптимизация: предыдущая страница замораживается и при
возвращении на неё
моментально восстанавливается.
Особенности:
— Сохраняется вместе с JavaScript Heap

15.

дека гугла
про заморозку
youtu.be/5b4YcLB4DVI?t=346

16.

Определен
ие
BFCache
Back Forward Cache (сокр. BFCache) — это браузерная
оптимизация: предыдущая страница замораживается и при
возвращении на неё
моментально восстанавливается.
Особенности:
— Сохраняется вместе с JavaScript Heap
— Работает в пределах одной вкладки

17.

Видео: Кнопка
«Назад» → к старой
вкладке

18.

Определен
ие
BFCache
Back Forward Cache (сокр. BFCache) — это браузерная
оптимизация: предыдущая страница замораживается и при
возвращении на неё
моментально восстанавливается.
Особенности:
— Сохраняется вместе с Javascript Heap
— Работает в пределах одной вкладки
— Хранится в оперативной памяти

19.

Как переходят
назад-вперёд
Кнопка «Назад» или «Вперёд»
в браузере
1. Сочетание клавиш: ⌘← или ⌘→
1. Жест на трекпаде и экране:
свайп вправо или влево
1. На Андроиде стандартная кнопка
навигации
1. Из кода: history.back() и history.forward()
1.

20.

Сколько страниц
хранится
в BFCache
Сколько хватит памяти

21.

Время
жизни
страницы
3 ми
в BFCache
н
−4 %
в Хромиуме
bugs.chromium.org/p/chromium/issues/detail?id=1305878

22.

Время
жизни
страницы
30 мин
1 ми
в BFCache
0 н
−4 %
в Хромиуме
bit.ly/bfcached-page-lifetime-in-chromium
bit.ly/bfcached-page-lifetime-in-safari
в Веб-ките и Файерфоксе

23.

Время
жизни
страницы
30 мин
1 ми
в BFCache
0 н
−4 %
в Хромиуме
bit.ly/bfcached-page-lifetime-in-firefox
в Веб-ките и Файерфоксе

24.

История
появления
BFCache
Back Forward Cache (сокр. BFCache) — это браузерный
кеш, в котором сохраняется предыдущая страница c кучей
задач Javascript в пределах одной вкладки
2002 Page
Cache
2009
Обновление
2019
Ренейминг
2005 Релиз
v1.5
2020 Частично
v86
2021 Релиз
v96

25.

Поддержка
BFCache
в WebView

26.

BFCache — это стандартная
функциональность браузера

27.

Legacy code
BFCache — это стандартная
работа браузера

28.

Почему у вас
не работает
BFCache и как
его включить
Определение блокеров в инструментах разработчика,
поиск и исправление проблемного кода

29.

Не надо смотреть на
полосу загрузки

30.

История
Виктор Хомяков. BFCache, или Туда и обратно

31.

История

32.

unload в аудите
Лайтхауса
developer.chrome.com/docs/lighthouse/performance/bf-cache/

33.

Почему
Обработчик unload

34.

Почему
BFCache
не работал
на Маркете
The page has an unload handler
in a subframe.
2. Pages whose main resource has cachecontrol:no-store cannot enter back/forward
cache.
3. Pages that use Media Device Dispatcher
are not eligible for back/forward cache.
4. Pages that have requested sensor
permissions are not currently eligible for
back/forward cache.
5. Pages that use SpeechSynthesis are not
currently eligible for back/forward cache.
1.

35.

Почему BFCache
не работал на
Маркете

36.

Почему
Обработчик unload
Дев-тулзы для BFCache

37.

Причина
1. Обработчик
unload

38.

Статистика
использования
unload
almanac.httparchive.org/en/2022/performance#fig-35

39.

Как используется unload
Событие unload вызывается, когда пользователь закрывает
страницу. На unload отправляют аналитику, сохраняют
пользовательские данные, что-то финализируют и чистят

40.

Почему unload мешает
BFCache
C unload всё не так однозначно

41.

Unload
не так страшен
Если
Не мешает в мобильных браузерах
Не мешает в десктопном Сафари
Не будет мешать в будущем:
— BFCache-Opt-In: unload
github.com/fergald/docs/blob/master/explainers/permissions-policy-deprecate-unload.md
— Document-Policy: unload=?0
— Permissions-Policy: unload=()

42.

Что сделать с unload
window.onunload
Событие unload вызывается, когда пользователь
закрывает = () => {...};
window.addEventListener('unload',
...)
страницу. Обычно unload отправляет соответствующее

событие в метрику или очищает данные на странице.
window.onpagehide = () => {...};
window.addEventListener('pagehide', ...)
Если вызвать unload при попадании в BFCache,
то залогируется лишнее событие в метрику или страница
может развалиться.
Если не вызвать unload при попадании в BFCache, то
событие может никогда не обработаться, например, когда
пользователь закроет браузер.
Вызвать unload при закрытии браузера
со страницы в BFCache — сложно.

43.

Что сделать с unload
Событие unload вызывается, когда пользователь закрывает
страницу. Обычно unload отправляет соответствующее
событие в метрику или очищает данные на странице.
Если вызвать unload при попадании в BFCache,
то залогируется лишнее событие в метрику или страница
может развалиться.
Если не вызвать unload при попадании в BFCache, то
событие может никогда не обработаться, например, когда
пользователь закроет браузер.
Вызвать unload при закрытии браузера
со страницы в BFCache — сложно.
Хром для разработчиков. Page Lifecycle API

44.

Причина
1. Обработчик
unload

45.

Что сделать с unload
// Игнорируем window.onunload
Object.defineProperty(window, 'onunload', {set: function(handler) {}});
if (!window.original_addEventListener) {
window.original_addEventListener = window.addEventListener;
function addEventListener_monkeypatch(event_type, handler, opt) {
// Игнорируем
unload
Событие
unloadобработку
вызывается,
когда пользователь закрывает
if
(event_type
!==
'unload')
{
страницу. Обычно unload отправляет соответствующее
this.original_addEventListener(event_type,
handler,
opt);
событие в метрику или очищает данные на странице.
} else {
// Мы
также можем
за обработчиками
Если
вызвать
unload следить
при попадании
в BFCache,unload
handlers
were
used'); или страница
то console.trace('unload
залогируется лишнее
событие
в метрику
}
может
развалиться.
}
window.addEventListener
addEventListener_monkeypatch;
Если
не вызвать unload =
при
попадании в BFCache, то
}событие может никогда не обработаться, например, когда
пользователь закроет браузер.
Вызвать unload при закрытии браузера
со страницы в BFCache — сложно.
chikamune-cr.github.io/how_to_disable_unload_handlers_by_monkeypatch/

46.

Что сделать с unload
Событие unload вызывается, когда пользователь закрывает
страницу. Обычно unload отправляет соответствующее
событие в метрику или очищает данные на странице.
Если вызвать unload при попадании в BFCache,
то залогируется лишнее событие в метрику или страница
может развалиться.
Если не вызвать unload при попадании в BFCache, то
событие может никогда не обработаться, например, когда
пользователь закроет браузер.
Вызвать unload при закрытии браузера
со страницы в BFCache — сложно.

47.

Что сделать с unload
Событие unload вызывается, когда пользователь закрывает
страницу. Обычно unload отправляет соответствующее
событие в метрику или очищает данные на странице.
Если вызвать unload при попадании в BFCache,
то залогируется лишнее событие в метрику или страница
может развалиться.
Если не вызвать unload при попадании в BFCache, то
событие может никогда не обработаться, например, когда
пользователь закроет браузер.
Вызвать unload при закрытии браузера
со страницы в BFCache — сложно.

48.

Как мы заменяли unload
в чужих скриптах
Если
⭐ Уровень Внутри Маркета. GTM
⭐⭐ Уровень Вне Маркета. UXFeedback
⭐⭐⭐ Уровень Вне Яндекса. Adriver

49.

Что сделать с beforeunload
function beforeUnloadListener(event) {
event.preventDefault();
Событие unload вызывается, когда пользователь закрывает
return event.returnValue
= 'Are you sure
you want to exit?';
страницу.
Обычно unload отправляет
соответствующее
};
событие в метрику или очищает данные на странице.
window.addEventListener('beforeunload', (event) => {
Если
вызвать unload при попадании
if (pageHasUnsavedChanges())
{ в BFCache,
то залогируется
лишнее событие в метрику или страница
beforeUnloadListener(event);
может
развалиться.
}
});
Если не вызвать unload при попадании в BFCache, то
событие может никогда не обработаться, например, когда
пользователь закроет браузер.
Вызвать unload при закрытии браузера
со страницы в BFCache — сложно.

50.

Что сделать с beforeunload
function beforeUnloadListener(event) {
event.preventDefault();
Событие unload вызывается, когда пользователь закрывает
return event.returnValue
= 'Are you sure
you want to exit?';
страницу.
Обычно unload отправляет
соответствующее
};
событие в метрику или очищает данные на странице.
onPageHasUnsavedChanges(() => {
Если
вызвать unload при попадании в BFCache,
window.addEventListener('beforeunload',
beforeUnloadListener);
то залогируется лишнее событие в метрику или страница
});
может развалиться.
onAllChangesSaved(()
=> {
window.removeEventListener('beforeunload',
beforeUnloadListener);
Если не вызвать unload при попадании в BFCache, то
});
событие может никогда не обработаться, например, когда
пользователь закроет браузер.
Вызвать unload при закрытии браузера
со страницы в BFCache — сложно.

51.

Почему
Обработчик unload

52.

Причина 2.
Заголовок Cache-Control: nostore

53.

Статистика
использования
Cache-Control: nostore
almanac.httparchive.org/en/2022/performance#fig-35

54.

Как используется заголовок
Cache-Control: no-store
Значение no-store говорит браузеру не хранить ответы сервера в
HTTP-кеше

55.

Почему значение no-store
мешает BFCache
Исторически сложилось, что влияет на BFCache
github.com/fergald/explainer-bfcache-ccns/blob/main/README.md

56.

Как используется заголовок
и почему он мешает
BFCache
Значение no-store говорит браузеру, не хранить ответы сервера
в HTTP-кеше
Cache-Control: no-store

Cache-Control: no-cache или Cache-Control: max-age=0
almanac.httparchive.org/en/2022/performance#fig-35
27 сен 2019. trac.webkit.org/changeset/250437/webkit/

57.

Как используется заголовок
и почему он мешает
BFCache
Значение no-store говорит браузеру, не хранить ответы сервера
в HTTP-кеше
Cache-Control: no-store

Cache-Control: no-cache или Cache-Control: max-age=0
almanac.httparchive.org/en/2022/performance#fig-35
2 мар 2020. trac.webkit.org/changeset/257714/webkit/

58.

Как используется заголовок
и почему он мешает
BFCache
Значение no-store говорит браузеру, не хранить ответы сервера
в HTTP-кеше
Cache-Control: no-store

Cache-Control: no-cache или Cache-Control: max-age=0
almanac.httparchive.org/en/2022/performance#fig-35
12 июня 2020. trac.webkit.org/changeset/262978/webkit/

59.

Что сделать с no-store
Событие unload вызывается, когда пользователь закрывает
Cache-Control:
no-store
страницу. Обычно unload
отправляет соответствующее
событие в метрику или ↓
очищает данные на странице.
Если вызвать unloadno-cache
при попадании
Cache-Control:
илив BFCache,
maxто
залогируется
лишнее
событие
в
метрику
или
страница
age=0
может развалиться.
Если не вызвать unload при попадании в BFCache, то
событие может никогда не обработаться, например, когда
пользователь закроет браузер.
Вызвать unload при закрытии браузера
со страницы в BFCache — сложно.
almanac.httparchive.org/en/2022/performance#fig-35
developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control

60.

Почему
Обработчик unload

61.

Причина 3.
Сложные браузерные АПИ
WebSockets, WebRTC, Web Locks,
IndexedDB

62.

Статистика
использования
WebSockets API
chromestatus.com/metrics/feature/timeline/popularity/1149

63.

Как используются сложные
АПИ
Сложные браузерные АПИ помогают делать чаты
и видеотрансляции, хранить данные в браузере и многое другое

64.

Почему сложные АПИ
мешают BFCache
Проектировались без учёта BFCache
Пока не понятно, что с ними делать
Могут общаться с другими вкладками в браузере
almanac.httparchive.org/en/2022/performance#fig-35

65.

Что сделать
с современными АПИ

66.

Как используется заголовок
и почему он мешает
BFCache
Значение no-store говорит браузеру, не хранить ответы сервера
в HTTP-кеше
Cache-Control: no-store

Cache-Control: no-cache или Cache-Control: max-age=0

67.

Как используется заголовок
и почему он мешает
BFCache
Значение no-store говорит браузеру, не хранить ответы сервера
в HTTP-кеше
Cache-Control: no-store

Cache-Control: no-cache или Cache-Control: max-age=0
almanac.httparchive.org/en/2022/performance#fig-35
MDN. Web API. Media Capture and Streams API. MediaDevices

68.

Что сделать
с современными АПИ
Событие unload вызывается, когда пользователь закрывает
страницу. Обычно unload отправляет соответствующее
событие в метрику или очищает данные на странице.
// fp.js
navigator.mediaDevices
Если вызвать unload при попадании в BFCache,
то залогируется лишнее событие в метрику или страница
может развалиться.
Если не вызвать unload при попадании в BFCache, то
событие может никогда не обработаться, например, когда
пользователь закроет браузер.
Вызвать unload при закрытии браузера
со страницы в BFCache — сложно.

69.

Что сделать
с современными АПИ

70.

Как используется заголовок
и почему он мешает
BFCache
Значение no-store говорит браузеру, не хранить ответы сервера
в HTTP-кеше
Cache-Control: no-store

Cache-Control: no-cache или Cache-Control: max-age=0

71.

Как используется заголовок
и почему он мешает
BFCache
Значение no-store говорит браузеру, не хранить ответы сервера
в HTTP-кеше
Cache-Control: no-store

Cache-Control: no-cache или Cache-Control: max-age=0
almanac.httparchive.org/en/2022/performance#fig-35
MDN. Web API. Sensor APIs

72.

Как используется заголовок
и почему он мешает
BFCache
Значение no-store говорит браузеру, не хранить ответы сервера
в HTTP-кеше
Cache-Control: no-store

Cache-Control: no-cache или Cache-Control: max-age=0
almanac.httparchive.org/en/2022/performance#fig-35
MDN. Web API. Permissions API

73.

Что сделать
с современными АПИ
Событие unload вызывается, когда пользователь закрывает
страницу. Обычно unload отправляет соответствующее
событие в метрику или очищает данные на странице.
// fp.js
navigator.permissions
Если вызвать unload при попадании в BFCache,
то залогируется лишнее событие в метрику или страница
может развалиться.
Если не вызвать unload при попадании в BFCache, то
событие может никогда не обработаться, например, когда
пользователь закроет браузер.
Вызвать unload при закрытии браузера
со страницы в BFCache — сложно.

74.

Что сделать
с современными АПИ
Событие unload вызывается, когда пользователь закрывает
страницу. Обычно unload отправляет соответствующее
событие в метрику или очищает данные на странице.
// fp.js
// Не используется. Оставлено
Если вызвать unload при попадании в BFCache,
для
сохранения
длины
логаили страница
то залогируется
лишнее событие
в метрику
может
развалиться.
permissions: (): boolean => true,
Если не вызвать unload при попадании в BFCache, то
событие может никогда не обработаться, например, когда
пользователь закроет браузер.
Вызвать unload при закрытии браузера
со страницы в BFCache — сложно.

75.

Причина 4.
Доступ к window.opener

76.

Почему
Обработчик unload

77.

Как используется заголовок
и почему он мешает
BFCache
Значение no-store говорит браузеру, не хранить ответы сервера
в HTTP-кеше
Cache-Control: no-store

Cache-Control: no-cache или Cache-Control: max-age=0

78.

Что сделать
с современными АПИ
Событие unload вызывается, когда пользователь закрывает
<head>
страницу. Обычно unload отправляет соответствующее
событие
... в метрику или очищает данные на странице.
<script>
Если
вызвать unload при попадании в BFCache,
то залогируется
лишнее событие в метрику или страница
delete window.Accelerometer;
может развалиться.
delete window.speechSynthesis;
Если
не вызвать unload при попадании в BFCache, то
</script>
событие может никогда не обработаться, например, когда
</head>
пользователь закроет браузер.
Вызвать unload при закрытии браузера
со страницы в BFCache — сложно.
almanac.httparchive.org/en/2022/performance#fig-35
developer.mozilla.org/en-US/docs/Web/API/Sensor_APIs#permissions_and_permissions_policy

79.

Что сделать
с современными АПИ
Событие unload вызывается, когда пользователь закрывает
страницу. Обычно unload отправляет соответствующее
событие в метрику или очищает данные на странице.
Permissions-Policy:
unload=(),
Если вызвать unload при попадании в BFCache,
accelerometer=(),
то залогируется лишнее событие в метрику или страница
может развалиться.
gyroscope=(),
magnetometer();
Если не вызвать unload
при попадании в BFCache, то
событие может никогда не обработаться, например, когда
пользователь закроет браузер.
Вызвать unload при закрытии браузера
со страницы в BFCache — сложно.
almanac.httparchive.org/en/2022/performance#fig-35
developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Permissions-Policy

80.

Почему
Обработчик unload
А что делать
с WebSockets?
Может, есть вариант
с IndexedDB?

81.

«Закрываем за собой
дверь»
let dbPromise;
function openDB() {
if (!dbPromise) {
dbPromise = new Promise((resolve, reject) => {
const req = indexedDB.open('my-db', 1);
Событие
unload вызывается,
когда пользователь закрывает
req.onupgradeneeded
= () => req.result.createObjectStore('keyval');
req.onerror =Обычно
() => reject(req.error);
страницу.
unload отправляет соответствующее
req.onsuccess = () => resolve(req.result);
событие
в метрику или очищает данные на странице.
});
}
return dbPromise;
Если
вызвать unload при попадании в BFCache,
}
тоОтключаемся
залогируется
лишнее
событие
метрику
или
страница
//
от БД, когда
пользователь
ушёл навдругую
страницу,
переключил
вкладку или окно
window.addEventListener('pagehide',
() => {
может развалиться.
if (dbPromise) {
dbPromise.then(db => db.close());
Если
не =вызвать
unload при попадании в BFCache, то
dbPromise
null;
}
событие
может никогда не обработаться, например, когда
});
пользователь
закроет
браузер.
//
Подключаемся к БД,
когда пользователь
открыл страницу, вернулся или переключился на неё
window.addEventListener('pageshow', () => openDB());
Вызвать unload при закрытии браузера
со страницы в BFCache — сложно.

82.

Почему
Обработчик unload

83.

Причина 4.
Доступ к window.opener

84.

Статистика
использования
window.opener
chromestatus.com/metrics/feature/timeline/popularity/4124

85.

Как используется opener
window.opener — ссылка на window предыдущей страницы.
С помощью этого получают данные о ней

86.

window.opener позволяет управлять предыдущей страницей
almanac.httparchive.org/en/2022/performance#fig-35
mathiasbynens.github.io/rel-noopener/

87.

Что сделать
с opener
Событие unload вызывается, когда пользователь закрывает
window.open(url)
страницу. Обычно unload отправляет соответствующее
событие
в
метрику
или
очищает
данные
на
странице.

Если вызвать unload приnull,
попадании в BFCache,
window.open(url,
то залогируется лишнее событие в метрику или страница
'noopener')
может развалиться.
или
Если не вызвать unload при попадании в BFCache, то
<a
href={url}
target="_blank"
событие
может никогда
не обработаться, например, когда
пользователь закроет />
браузер.
rel="noopener"
Вызвать unload при закрытии браузера
со страницы в BFCache — сложно.

88.

Как используется заголовок
и почему он мешает
BFCache
Значение no-store говорит браузеру, не хранить ответы сервера
в HTTP-кеше
Cache-Control: no-store

Cache-Control: no-cache или Cache-Control: max-age=0
almanac.httparchive.org/en/2022/performance#fig-35
Гугл-док. bit.ly/bfcache-not-restored-reasons

89.

Как используется заголовок
и почему он мешает
BFCache
Значение no-store говорит браузеру, не хранить ответы сервера
в HTTP-кеше
Cache-Control: no-store

Cache-Control: no-cache или Cache-Control: max-age=0
almanac.httparchive.org/en/2022/performance#fig-35
github.com/WebKit/WebKit/blob/main/Source/WebCore/history/BackForwardCache.cpp#L282

90.

Иногда сам интерфейс может блокировать
моментальный переход назад

91.

причина 5 для
кнопки назад
используется ссыл

92.

Добавление onClick ссылке
для моментального возврата
function buildGoBackOnClick() {
if (!document.referrer) return;
return (event) => {
event.preventDefault();
history.back();
};
}

93.

Добавление onClick ссылке
для моментального возврата
function buildGoBackOnClick(prevPathnames) {
if (!document.referrer) return;
const refPathname = new URL(document.referrer).pathname;
const wasReferrerActualPreviousPage = prevPathnames.some(
(prevPathname) => refPathname.includes(`/${prevPathname}`)
);
if (wasReferrerActualPreviousPage) {
return (event) => {
event.preventDefault();
history.back();
};
}
}

94.

Почему у вас
перестал
работать сайт
из-за BFCache
Странные данные на предыдущей странице,
полезные хелперы для решения проблем

95.

load → pageshow в
новых скриптах,
когда нужно.

96.

Чиним актуализацию
после возвращения на чекаут
fromEvent(window, 'load').pipe(
map(() => actualizeCartGroup({cartGroupId})),
);

fromEvent(window, 'pageshow').pipe(
map(() => actualizeCartGroup({cartGroupId})),
);

97.

старое значение в
поле поиска

98.

Добавление onClick ссылке
для моментального возврата
// RxJS-хук для старта эпика,
// когда страница досталась из BFCache
export const bfcache: () =>
Observable<PageTransitionEvent> = () =>
fromEvent(window, "pageshow").pipe(
filter((event) => event.persisted)
);

99.

Добавление
onClick
ссылке
// Пример использования RxJS-хука
export
constмоментального
onSetActualTextFromBFCache = () => возврата
для
bfcache().pipe(
switchMap(() => {
const urlParams = new URLSearchParams(window.location.search);
const text = urlParams.get('text');
input.value = text;
return of(changeText({text}));
});
);

100.

старое значение в
поле поиска

101.

Добавление onClick ссылке
для моментального возврата
// React-хук для обновления данных,
// когда страница досталась из BFCache
export const useUpdateAfterBFCache = (doSmth: () => void): void => {
useEffect(() => {
const handlePageshow = (event: PageTransitionEvent) => {
if (event.persisted) {
doSmth();
}
};
window.addEventListener('pageshow', handlePageshow);
return () => {
window.removeEventListener('pageshow', handlePageshow);
};
}, []);
};

102.

при разлогине с
Избранного
а если у вас банк,

103.

при разлогине с
Избранного
а если у вас банк,

104.

/**
* Перезагружаем страницу,
* если пользователь вышел из аккаунта и вернулся назад
*
* Объяснение
* На предыдущей странице состояние JavaScript без изменений,
* поэтому оно только говорит, что юзер был авторизован (wasAuth).
* Актуальная проверка на авторизацию — по куке или запросу к TVM
*/
const checkLogout = () => {
const isAuthAfterBFCache = document.cookie.match(/my-login/);
if (wasAuth && !isAuthAfterBFCache) {
document.body.innerHTML = '';
location.reload();
}
}
useUpdateAfterBFCache(checkLogout);
Добавление onClick ссылке
для моментального возврата

105.

при разлогине с
Избранного
а если у вас банк,

106.

при разлогине с
Избранного
а если у вас банк,

107.

при разлогине с
Избранного
а если у вас банк,

108.

Как используется заголовок
и почему он мешает
BFCache
Значение no-store говорит браузеру, не хранить ответы сервера
в HTTP-кеше
Cache-Control: no-store

Cache-Control: no-cache или Cache-Control: max-age=0
almanac.httparchive.org/en/2022/performance#fig-35
github.com/WebKit/WebKit/blob/main/Source/WebCore/history/BackForwardCache.cpp#L282

109.

Что делать,
чтобы
не сломали
BFCache
Автотест, график, рассказ коллегам

110.

Сквозной (e2e) тест
// bfcache.test.js
await this.browser.yaOpenPage('product', data);
await this.browser.waitUntil(async () => {
const readyState = await this.browser
.execute(() => document.readyState)
.then(({ value }) => value);
return readyState === "complete";
});

111.

Сквозной (e2e) тест
// Продолжение bfcache.test.js
await this.browser.yaOpenPage('product-reviews');
await this.browser.execute(() => history.back());

112.

Сквозной (e2e) тест
// Продолжение bfcache.test.js
await this.browser.yaOpenPage('product-reviews');
await this.browser.execute(() => history.back());
// Проверяем, что страница досталась из BFCache

113.

Сквозной (e2e) тест
// Продолжение bfcache.test.js
await this.browser.yaOpenPage('product-reviews');
await this.browser.execute(() => history.back());
// Проверяем, что страница досталась из BFCache
// ?
const navigationType = await this.browser
.execute(() => performance.getEntriesByType(
"navigation"
)[0].type)
.then(({ value }) => value);

114.

Сквозной (e2e) тест
// Продолжение bfcache.test.js
await this.browser.yaOpenPage('product-reviews');
await this.browser.execute(() => history.back());
// Проверяем, что страница досталась из BFCache
// Будет всегда возвращать "back_forward"
const navigationType = await this.browser
.execute(() => performance.getEntriesByType(
"navigation"
)[0].type)
.then(({ value }) => value);
Navigation Timing Level 2. NavigationTimingType enum

115.

Сквозной (e2e) тест
// Продолжение bfcache.test.js
await this.browser.yaOpenPage('product-reviews');
await this.browser.execute(() => history.back());
// Проверяем, что страница досталась из BFCache
await this.browser.waitUntil(
async () => this.browser
.execute(() => window._isPageFromBFCache)
.then(({ value }) => Boolean(value)),
5000,
"Страница не открылась моментально из BFCache. " +
"Cм. bit.ly/bfcache-not-restored-reasons"
);

116.

Сквозной (e2e) тест
// App.js
// Готовим проверочное поле для теста
const markBFCache = () => {
window._isPageFromBFCache = true;
};
useUpdateAfterBFCache(markBFCache);

117.

Ручной тест
— Расписанный по шагам тест-кейс: A → B → A
— Еженедельный регресс
— Регресс внешними асессорами

118.

График хитов

119.

Добавление onClick ссылке
для моментального возврата
/**
* Отправить метрику, что страница из BFCache
* @see https://web.dev/bfcache/
*/
function sendBFCacheStatus() {
window.addEventListener('pageshow', (event) => {
if (event.persisted) {
sendCounter('bfcache');
}
});
}

120.

BFCache notRestoredReasons
API
function returnNRR() {
const navEntries = performance.getEntriesByType("navigation");
for (let i = 0; i < navEntries.length; i++) {
console.log(`Navigation entry ${i}`);
let navEntry = navEntries[i];
console.log(navEntry.notRestoredReasons);
}
}
developer.chrome.com/docs/web-platform/bfcache-notrestoredreasons/

121.

Плагин для линтера
github.com/ilyasidorchik/eslint-plugin-bfcache

122.

Рассказ
коллегам
support.apple.com/ru-ru/HT204948

123.

Плагин для линтера

124.

Рассказ для коллег

125.

Плагин для линтера

126.

Рассказ для коллег

127.

Как BFCache
влияет
на метрики
Ухудшение и адаптация продуктовых и скоростных метрик

128.

Продуктовые метрики
просядут
−6
%
визитов корзины
−4
%
визитов чекаута

129.

Добавление
onClick
ссылке
// Отправляем визит страницы
при первой
загрузке
gtag("event",
"page_view");
для
моментального
возврата
window.addEventListener("pageshow", (event) => {
// Отправляем ещё один визит страницы
// при восстановлении из BFCache
if (event.persisted) {
gtag("event", "page_view");
}
});

130.

Скоростные метрики
тоже просядут

131.

Скоростные метрики
тоже просядут
Navigation Timing Level 2. NavigationTimingType enum

132.

Адаптируем FCP и LCP
под BFCache
onBFCacheRestore((event) => {
metric = initMetric('FCP'); // Также для LCP
report = bindReporter(onReport, metric, FCPThresholds);
doubleRAF(() => {
metric.value = performance.now() - event.timeStamp;
report(true);
});
});
github.com/GoogleChrome/web-vitals/blob/main/src/onFCP.ts

133.

Адаптируем CLS
под BFCache
onBFCacheRestore(() => {
metric = initMetric('CLS', 0);
report = bindReporter(onReport, metric, CLSThresholds);
doubleRAF(() => report());
});
github.com/GoogleChrome/web-vitals/blob/main/src/onCLS.ts

134.

Адаптируем CLS
под BFCache
youtu.be/mdB-J6BRReo?t=469

135.

мб скриншот Google
Search Console
support.google.com/webmasters/answer/9205520

136.

Видео: страница
открывается в новой
вкладке

137.

Видео: страница
открывается в новой
вкладке

138.

Видео: страница
открывается в новой
вкладке

139.

BFCache
Быстро, удобно, экономно
Стандартная функциональность браузера
Инструменты разработчика в Хроме
Замените unload на pagehide, no-store на
no-cache, отпишитесь от лишнего при
pagehide
Адаптируйте сайт под BFCache
Напишите тест, сделайте график,
расскажите коллегам
Адаптируйте метрики

140.

141.

Спасибо!
Илья Сидорчик, разработчик интерфейсов
Презентация в канале: t.me/sidorchikdev
English     Русский Rules