Настройка канала обновлений

Каналы обновлений (Update Channels) — это механизм управления версией сервис-воркера и контроля поведения приложения при выпуске новых версий. Workbox предоставляет инструменты для тонкой настройки этого процесса, позволяя автоматически или вручную управлять обновлениями и уведомлять клиентов о доступности новых ресурсов.


Регистрация сервис-воркера с поддержкой канала обновлений

Для организации канала обновлений необходимо правильно зарегистрировать сервис-воркер и настроить событие updatefound:

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/sw.js').then(registration => {
    registration.addEventListener('updatefound', () => {
      const newWorker = registration.installing;
      newWorker.addEventListener('statechange', () => {
        if (newWorker.state === 'installed') {
          if (navigator.serviceWorker.controller) {
            // Сервис-воркер обновился
            console.log('Новая версия доступна');
          } else {
            // Первый запуск сервис-воркера
            console.log('Контент доступен оффлайн');
          }
        }
      });
    });
  }).catch(error => {
    console.error('Ошибка при регистрации сервис-воркера:', error);
  });
}

Ключевые моменты:

  • updatefound срабатывает при появлении нового установочного воркера.
  • statechange позволяет отследить момент, когда воркер полностью установлен.
  • navigator.serviceWorker.controller проверяет, управляет ли текущий сервис-воркер страницей.

Workbox и автоматическая активация обновлений

Workbox поддерживает автоматическое обновление сервис-воркера через опцию skipWaiting():

import { precacheAndRoute } from 'workbox-precaching';
import { clientsClaim } from 'workbox-core';

precacheAndRoute(self.__WB_MANIFEST);

self.addEventListener('install', event => {
  self.skipWaiting();
});

self.addEventListener('activate', event => {
  clientsClaim();
});

Объяснение:

  • skipWaiting() заставляет новый сервис-воркер активироваться сразу после установки, минуя стандартный этап ожидания.
  • clientsClaim() позволяет новому воркеру сразу управлять всеми открытыми клиентами, обеспечивая моментальный доступ к новым ресурсам.

Настройка канала сообщений для уведомления клиентов

Для уведомления страниц о новой версии можно использовать postMessage. Workbox предоставляет удобный способ коммуникации через сервис-воркер:

self.addEventListener('message', event => {
  if (event.data && event.data.type === 'SKIP_WAITING') {
    self.skipWaiting();
  }
});

navigator.serviceWorker.addEventListener('controllerchange', () => {
  console.log('Сервис-воркер сменился. Перезагрузите страницу для применения обновлений.');
});

Особенности:

  • Канал сообщений позволяет вручную запускать skipWaiting() по запросу страницы.
  • Событие controllerchange информирует, что сервис-воркер обновился, и новые ресурсы готовы к использованию.

Использование Workbox Broadcast Update для автоматического уведомления

Workbox предоставляет модуль workbox-broadcast-update для автоматического уведомления клиентов о новых версиях кэшируемых ресурсов:

import { BroadcastUpdatePlugin } from 'workbox-broadcast-update';
import { StaleWhileRevalidate } from 'workbox-strategies';
import { registerRoute } from 'workbox-routing';

const broadcastUpdatePlugin = new BroadcastUpdatePlugin();

registerRoute(
  ({ request }) => request.destination === 'script' || request.destination === 'style',
  new StaleWhileRevalidate({
    cacheName: 'static-resources',
    plugins: [broadcastUpdatePlugin]
  })
);

Принципы работы:

  • Каждый раз, когда ресурс обновляется, BroadcastUpdatePlugin отправляет сообщение на все открытые страницы.
  • Страницы могут подписаться на эти сообщения, чтобы отображать уведомление о новой версии или автоматически перезагружать страницу.
navigator.serviceWorker.addEventListener('message', event => {
  if (event.data && event.data.type === 'CACHE_UPDATED') {
    console.log(`Файл ${event.data.meta.url} обновлён`);
  }
});

Создание разных каналов обновлений для критических и некритических ресурсов

Для управления обновлениями можно разделять ресурсы на категории:

registerRoute(
  ({ request }) => request.destination === 'image',
  new StaleWhileRevalidate({
    cacheName: 'images',
    plugins: [
      new BroadcastUpdatePlugin({
        channelName: 'images-update'
      })
    ]
  })
);

registerRoute(
  ({ request }) => request.destination === 'script',
  new StaleWhileRevalidate({
    cacheName: 'scripts',
    plugins: [
      new BroadcastUpdatePlugin({
        channelName: 'scripts-update'
      })
    ]
  })
);

Важное отличие:

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

Стратегия контроля версий и задержка применения обновлений

Иногда необходимо, чтобы обновления не применялись немедленно. Можно комбинировать skipWaiting и логику проверки версии:

self.addEventListener('install', event => {
  event.waitUntil(
    caches.open('versioned-cache-v2').then(cache => {
      return cache.addAll(['/index.html', '/main.js']);
    })
  );
});

self.addEventListener('activate', event => {
  event.waitUntil(
    caches.keys().then(keys => {
      return Promise.all(
        keys
          .filter(key => key !== 'versioned-cache-v2')
          .map(key => caches.delete(key))
      );
    })
  );
});

Принципы:

  • Новый воркер создаёт отдельный кэш для версии.
  • Старые версии удаляются только после активации нового воркера.
  • Это позволяет контролировать момент применения обновлений и минимизировать внезапные изменения для пользователей.