Перейти к содержанию

Жизненный цикл service worker

Manifest описал представление offline-lab, но ещё не управляет запросами. Теперь добавим service worker и сначала рассмотрим его состояния. Если сразу перейти к кешированию, легко принять зарегистрированный файл за уже работающий автономный режим. Различие состояний особенно важно при первом открытии и обновлении приложения.

Service worker исполняется отдельно от страницы и может участвовать в обработке запросов управляемых клиентов. Его жизнь не совпадает с жизнью одной вкладки. Он не имеет обычного DOM и не предназначен для постоянного хранения переменных в памяти. Браузер способен остановить его между событиями, поэтому долговременные сведения сохраняются явным способом.

Регистрация и область

Файл sw.js положим в корень отдельного учебного origin. Регистрация с областью / включает знакомые пути Markdown и XML. Расположение файла и разрешённая область имеют значение: нельзя произвольно расширить scope вложенного worker без соответствующих условий сервера. Регистрация service worker.

Полный начальный assets/register-worker.js только регистрирует worker и показывает состояния. Он ничего не сохраняет и не принуждает страницу к перезагрузке. Подключите его как module в общей оболочке учебных страниц, чтобы прямой вход в любой документ имел одинаковую начальную логику.

if ("serviceWorker" in navigator) {
  navigator.serviceWorker.register("/sw.js", {scope: "/"})
    .then(registration => {
      console.info("worker scope", registration.scope);
      const observed = new WeakSet();
      function observe(worker) {
        if (!worker || observed.has(worker)) return;
        observed.add(worker);
        console.info("worker state", worker.state);
        worker.addEventListener("statechange", () => {
          console.info("worker state", worker.state);
        });
      }
      observe(registration.installing);
      observe(registration.waiting);
      observe(registration.active);
      registration.addEventListener("updatefound", () => {
        observe(registration.installing);
      });
    })
    .catch(error => console.warn("Worker registration failed", error));
  navigator.serviceWorker.addEventListener("controllerchange", () => {
    console.info("controller", navigator.serviceWorker.controller?.scriptURL);
  });
} else {
  console.info("Ordinary website mode");
}

В head или конце body нужен элемент <script type="module" src="/assets/register-worker.js"></script>. Это часть оболочки, а не команда для выполнения в терминале. Если API недоступен, обычное чтение остаётся возможным. Отсутствие worker не должно удалять ссылки или превращать HTML в пустой экран.

Для этого урока полный sw.js содержит только обработчики состояния. В следующем он будет заменён целиком более содержательной версией.

self.addEventListener("install", () => {
  console.info("offline-lab install");
});
self.addEventListener("activate", () => {
  console.info("offline-lab activate");
});

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

Установка и активация

Во время установки worker может подготовить нужные ресурсы. Для асинхронной работы в этом событии используется event.waitUntil, который связывает успешность этапа с обещанием завершения. Если необходимая подготовка завершается ошибкой, новая версия не должна считаться установленной. Конкретное сохранение добавим после понимания состояний.

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

Поле registration.waiting позволяет увидеть ожидающий экземпляр. Поле registration.active относится к активному worker регистрации. У текущей страницы отдельно существует navigator.serviceWorker.controller. Эти сведения связаны, но не тождественны: активный worker может существовать, пока уже открытая страница ещё не управляется им.

Ожидаемый сценарий первой практики: браузер получает файл, установка завершается, появляется активная регистрация, а последующая навигация в области может получить controller. Точный вид диагностического интерфейса зависит от браузера. Нельзя по одному сообщению activate объявить, что текущая вкладка уже проходит через новый обработчик fetch.

Контроллер страницы

navigator.serviceWorker.ready помогает дождаться активной регистрации для области. Однако разрешение этого Promise не является универсальным подтверждением controller текущего документа. Для нашей проверки эти поля рассматриваются отдельно. Если открытие произошло до регистрации, новая навигация может понадобиться для привычного управления страницей.

Иногда разработчики вызывают clients.claim, чтобы активный worker начал управлять уже существующими подходящими клиентами. Это осознанное изменение поведения, а не обязательная строка любого примера. Оно может привести к тому, что документ, полученный без новой логики, начнёт делать запросы через неё. При обновлении нужно учитывать совместимость содержимого и обработчика.

Аналогично skipWaiting позволяет ускорить переход ожидающего worker. Но принудительная активация способна сменить программу запросов для страниц, которые ещё используют старые ресурсы. Поэтому в начальном проекте мы не добавляем обе функции по привычке. Управляемое обновление появится в отдельном уроке с объяснением состояния вкладок. Service Worker API.

Событие запроса

Обработчик fetch получает запросы в соответствующих условиях управления. Он может вернуть сетевой ответ или заранее сохранённый Response. Для первого автономного примера мы ограничимся GET-навигацией своего origin. Не нужно перехватывать внешний видеоплеер, аналитические библиотеки и любые запросы страницы без задачи и собственной модели ошибок.

Service worker не исправляет серверные ответы автоматически. Если старый .php-путь выдаётся с неправильным MIME, браузер получает неправильный документ ещё до полезной автономной логики. Сначала проверяется доставка готовой страницы. Затем можно сохранить правильный ответ и определить, что происходит при отсутствии сети.

Долговременная память программы тоже не находится в глобальном массиве worker. Между событиями экземпляр может быть остановлен. Если выбранные главы должны сохраняться после закрытия вкладки, потребуются Cache API и структурированные метаданные. Передача сообщений с обычной страницей помогает согласовать UI, но не делает память worker постоянной.

Что отличает Web Worker

В поисковом курсе Web Worker выполнял вычисления индекса вне основного потока страницы. Его задача была связана с конкретным компонентом и сообщениями. Service worker здесь имеет регистрацию, scope, жизненный цикл обновления и участие в запросах. Общая часть названия не позволяет переносить код одного окружения в другое без изменения архитектуры.

Поиск остаётся готовым компонентом. Наш worker будет обеспечивать доступность сохранённых документов и нужных ресурсов, а не считать релевантность. Если поиск зависит от сети, это ограничение показывается отдельно. Регистрация service worker не превращает удалённый API в локальный источник данных.

Для завершения урока сохраните ожидаемую последовательность состояний и источники будущих наблюдений: регистрация, installing/waiting/active, controller страницы. Не записывайте их как фактически полученные без своей проверки. Теперь можно добавить содержательную установку: подготовить один HTML-документ и вернуть его точный сохранённый ответ, когда сеть недоступна.