Перейти к содержанию
ERITKOM
ВебРазбор

Service Worker и офлайн: где хранятся ответы и почему обновление ждёт

Жизненный цикл фонового посредника, разные стратегии кэша, границы контроля страниц и отличие локальных данных от гарантированной резервной копии.

Установка service worker и заполнение кэша
Mozilla Contributors · CC BY-SA 2.5 · Изменены размер и формат.

Посредник запросов, а не скрытая вкладка

Service worker — отдельный событийный обработчик, зарегистрированный для origin и области URL. Он может перехватывать запросы страниц, которые контролирует, и возвращать ответы из сети, локального кэша либо собственной логики. Поэтому офлайн-режим представляет собой заданное поведение приложения, а не автоматическую копию всего однажды посещённого сайта.

Обработчик работает вне основного потока страницы и не имеет доступа к DOM. В нём недоступны синхронный XMLHttpRequest и Web Storage: взаимодействие строится вокруг асинхронных API. В частности, localStorage страницы не превращается в общую синхронную память фонового скрипта. Механизм доступен в безопасном контексте HTTPS; localhost имеет особый статус для разработки. Регистрация привязана к конкретному origin и области, а не ко всем вкладкам пользователя. Это ограничивает, какие запросы и документы могут участвовать в выбранном офлайн-поведении, даже если один сервер обслуживает несколько приложений.

Установка готовит новую версию

Сначала браузер получает скрипт и запускает установку. Обработчик install может подготовить необходимый набор ресурсов, например оболочку интерфейса. Успешно установленная версия и активная версия — не всегда одна и та же. При первой регистрации активация происходит без ожидания завершения работы прежней версии, поскольку её ещё нет.

Кэширование во время установки — выбранная стратегия, а не встроенный список обязательных файлов. Браузер не угадывает, какие изображения, маршруты или данные нужны офлайн. Если подготовительная операция включена в жизненный цикл через waitUntil и завершается ошибкой, установка может не завершиться успешно. Схемы MDN отдельно показывают получение кода, заполнение кэша и переход к контролю клиентов.

Почему новая версия остаётся waiting

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

Обычная смена происходит после завершения использования старой версии её клиентами. В activate можно удалять больше не нужные ресурсы прежних кэшей. Метод skipWaiting просит пропустить ожидание, однако сам по себе не переписывает уже загруженные HTML и JavaScript вкладки. Быстрая активация и согласованное обновление приложения поэтому не тождественны. Пользователь способен видеть старую страницу, чьи последующие запросы уже обрабатываются новой логикой; это отдельный случай совместимости ресурсов между выпусками.

Новая версия ожидает, пока старая контролирует открытую страницу
Новая версия ожидает, пока старая контролирует открытую страницу. Mozilla Contributors · CC BY-SA 2.5 · Изменены размер и формат.

Cache API не равен HTTP-кэшу

Cache хранит пары Request и Response и доступен как worker, так и обычному окну. Для одного origin могут существовать несколько именованных кэшей. Имя вроде версии приложения — соглашение самого приложения: API не понимает, что означает номер, и не удаляет предыдущий набор после появления следующего.

Записи не обновляются сами по себе и не истекают по обычным правилам HTTP-кэширования. Заголовок Cache-Control нельзя считать готовой политикой хранения внутри Cache API. Добавление, поиск и удаление выполняются явно. При сопоставлении запросов имеет значение также Vary у сохранённого ответа. Методы add и addAll получают ресурс и сохраняют результат, тогда как put принимает уже имеющуюся пару запроса и ответа. Вызов fetch возвращает Response и при HTTP-ошибке вроде 404; такая ошибка не обязана попасть в обработчик исключения. Поэтому сетевой сбой и ответ сервера с неуспешным статусом — разные результаты.

Три стратегии отвечают на разные вопросы

Cache first сначала ищет сохранённый ответ и обращается к сети при отсутствии записи. Такой подход может быстро показывать неизменяемые ресурсы оболочки, но сам факт попадания в кэш не подтверждает свежесть данных. Network first сначала обращается к серверу, а при неудаче использует локальный ответ. В этом случае актуальность важнее немедленного показа сохранённой версии.

Stale while revalidate возвращает доступную запись и параллельно обновляет кэш из сети. Полученный свежий ответ обычно пригодится последующим обращениям, а не обязательно заменит уже показанный экран. Эти варианты могут сосуществовать в одном приложении для разных ресурсов. Нельзя вывести универсальную стратегию только из типа проекта. У изображения с версией в имени файла и списка недавно изменённых записей различаются требования к свежести. Отдельно определяется реакция, когда нет ни сети, ни подходящего сохранённого ответа: сам перехват запроса не создаёт содержимое отсутствующей страницы.

Откуда берётся ответ при наличии записи
СтратегияПервое действиеСетевая свежесть
Cache first Вернуть найденный кэш Нужна отдельная логика обновления
Network first Получить ответ сервера Кэш — резерв при сетевой неудаче
Stale while revalidate Показать кэш, запросить сеть параллельно Обновляется сохранённая запись

Источник: MDN: Caching

Диск браузера имеет собственную политику

Локальное хранилище обычно учитывается по origin, который включает схему, хост и порт. Два приложения под разными путями одного origin не получают автоматически независимые квоты. При встраивании в сторонние контексты браузер может дополнительно разделять данные на разделы, поэтому привычная модель одного сайта не описывает все случаи.

По умолчанию хранение best-effort зависит от доступного места и политики браузера. Пользователь может удалить данные сайта, а браузер — освободить пространство. Запрос persistent storage меняет условия автоматического вытеснения, но не запрещает владельцу устройства очистить данные. Механизм предоставления такого режима различается: где-то показывается запрос, где-то решение принимается на основе взаимодействия с сайтом. Поэтому кэш ответов не становится гарантированной резервной копией пользовательской работы. Данные, которые существуют только на устройстве, имеют другой риск потери, чем ресурсы, доступные для повторного получения с сервера после восстановления связи.

Активация и контроль открытого документа

Активный service worker может вызвать clients.claim(), чтобы стать контроллером подходящих клиентов в своей области. У страниц, которые после этого оказались под его контролем, возникает controllerchange. Это отличается от состояния, когда новый worker активен, но уже открытая страница ещё продолжает жить без него до следующей загрузки.

Claim не означает повторного исполнения всего документа через новый кэш. Страница могла первоначально получить ресурсы напрямую из сети либо через другую версию, а затем перейти под новый контроль для дальнейших запросов. Отсюда разделение трёх событий: получение разметки, активация worker и назначение контроллера. Они не обязаны произойти одновременно. Внешне приложение может выглядеть неизменным, хотя маршрут последующих запросов уже другой. Коммуникация между страницей и worker позволяет отразить это состояние в интерфейсе, но сам API не выбирает за приложение момент перезагрузки, формат уведомления или допустимость прерывания незавершённой пользовательской операции.

Новый клиент уже обслуживается второй версией worker
Новый клиент уже обслуживается второй версией worker. Mozilla Contributors · CC BY-SA 2.5 · Изменены размер и формат.

Источники

Принципы журнала и исправления.

ERITKOM / НОВЫЙ ПРОЕКТ

Расскажите о задаче и оставьте контакты. Мы свяжемся с вами, чтобы обсудить проект.

  1. 01 Контакты
  2. 02 Ваша задача