Service Worker

Прокси-скрипт между страницей и сетью: offline-кэш, push, фоновая синхронизация, ядро PWA.

Что это

Отдельный JS-поток, зарегистрированный для origin'а. Перехватывает fetch-запросы со страниц этого origin'а, может ответить из Cache API или сети. Не имеет доступа к DOM. Живёт независимо от вкладок — может работать ещё 30 секунд — 5 минут после закрытия всех вкладок. Lifecycle: installwaitingactivateidle/fetch/message.

Базовое использование

// main.js — регистрация
if ('serviceWorker' in navigator) {
  const reg = await navigator.serviceWorker.register('/sw.js', {
    type: 'module',  // если используешь import внутри
    scope: '/',
  });
  console.log('SW registered', reg.scope);
}

// sw.js — установка: прогрев кэша
const CACHE = 'v1';
const ASSETS = ['/', '/app.js', '/styles.css', '/offline.html'];

self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open(CACHE).then((cache) => cache.addAll(ASSETS))
  );
  self.skipWaiting; // активироваться немедленно
});

self.addEventListener('activate', (event) => {
  // Удалить старые кэши
  event.waitUntil(
    caches.keys().then((keys) =>
      Promise.all(keys.filter((k) => k !== CACHE).map((k) => caches.delete(k)))
    )
  );
  self.clients.claim; // взять контроль над уже открытыми вкладками
});

self.addEventListener('fetch', (event) => {
  // Cache-first стратегия
  event.respondWith(
    caches.match(event.request).then((cached) => cached || fetch(event.request))
  );
});

// Сообщения от вкладок
self.addEventListener('message', (event) => {
  if (event.data?.type === 'skipWaiting') self.skipWaiting;
});

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

Фаза Когда / что
install Один раз при новом SW. Прогрев кэша. event.waitUntil блокирует переход
waiting SW зарегистрирован, но старый ещё контролирует вкладки. skipWaiting ускоряет
activate Чистка старых кэшей. clients.claim берёт управление существующими вкладками
idle / fetch / message / push / sync Рабочий цикл

Главные API

API Что
caches.open(name)Cache Хранилище Request→Response
cache.addAll([urls]) Прогрев из массива URL
cache.match(req) / caches.match(req) Поиск ответа в кэше
clients.matchAll / clients.claim Доступ к вкладкам
self.registration.showNotification Push-нотификации
event.waitUntil(promise) Продлевает жизнь SW до резолва

Требования

  • HTTPS или localhost (иначе не зарегистрируется)
  • sw.js должен быть в том же origin
  • Scope ограничен папкой sw.js (/sw.js контролирует всё; /foo/sw.js — только /foo/*)

Подводные камни

  • install единоразовый — фикс на новой версии SW (меняй имя кэша или contents)
  • Между деплоями старый SW отдаёт старый код — нужна стратегия инвалидации (skipWaiting, clients.claim, версионирование)
  • console.log внутри SW не виден в обычной DevTools-консоли — открой chrome://serviceworker-internals или Application → Service Workers
  • В фазе install JS прекомпилируется на 100% — отсюда популярный трюк прогрева JS через SW
  • fetch events работают только для запросов в scope; навигация на cross-origin не перехватывается
  • BroadcastChannel не работает внутри SW — используй postMessage к клиентам

Используется в bootcamp

  • PWA-задачи (если будут) — offline-кэш статики
  • Подкаст-плеер мог бы кэшировать эпизоды для offline-прослушивания

Ссылки

🎓 Источники

См. также