Принудительное обновление Service Worker

Основы работы Service Worker

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


Механизм принудительного обновления

Sw-precache генерирует файл service-worker.js, в котором указаны все ресурсы для кэширования. Каждый раз при генерации Service Worker создаётся новый хеш содержимого, и браузер распознаёт изменение скрипта. Однако если изменения происходят редко или разработчик хочет немедленно обновить кэш, необходимо использовать методы активации нового Service Worker вручную.

Основные события в Service Worker для обновления:

  1. install – событие установки нового Service Worker.
  2. activate – событие активации, которое позволяет очистить старый кэш.
  3. message – событие, через которое можно инициировать принудительное обновление из клиентской части.

Ключевой подход заключается в том, чтобы использовать метод skipWaiting() в событии install и метод clients.claim() в событии activate.


Использование skipWaiting() и clients.claim()

self.addEventListener('install', function(event) {
  event.waitUntil(
    self.skipWaiting()
  );
});

self.addEventListener('activate', function(event) {
  event.waitUntil(
    caches.keys().then(function(cacheNames) {
      return Promise.all(
        cacheNames.map(function(cacheName) {
          // Очистка старых кэшей
          if (cacheName !== CURRENT_CACHE_NAME) {
            return caches.delete(cacheName);
          }
        })
      );
    }).then(() => self.clients.claim())
  );
});
  • skipWaiting() принудительно завершает фазу ожидания нового Service Worker, позволяя ему сразу перейти в активное состояние.
  • clients.claim() гарантирует, что все открытые вкладки начнут использовать новый Service Worker без необходимости перезагрузки страницы.

Принудительное обновление из клиентской части

Для инициирования обновления из веб-приложения можно использовать метод postMessage:

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.ready.then(function(registration) {
    registration.active.postMessage({ action: 'skipWaiting' });
  });
}

В Service Worker необходимо добавить обработчик сообщений:

self.addEventListener('message', function(event) {
  if (event.data && event.data.action === 'skipWaiting') {
    self.skipWaiting();
  }
});

Таким образом, новая версия Service Worker активируется немедленно, и кэш обновляется без ожидания закрытия всех вкладок.


Управление кэшем при обновлении

Sw-precache поддерживает стратегию cacheFirst, когда ресурсы берутся из кэша, если они доступны. При принудительном обновлении важно корректно управлять старыми кэшами:

const CURRENT_CACHE_NAME = 'my-app-cache-v2';

self.addEventListener('activate', function(event) {
  event.waitUntil(
    caches.keys().then(cacheNames =>
      Promise.all(
        cacheNames.map(cacheName => {
          if (cacheName !== CURRENT_CACHE_NAME) {
            return caches.delete(cacheName);
          }
        })
      )
    ).then(() => self.clients.claim())
  );
});

Присвоение уникальных имен кэшу (v1, v2, v3) позволяет отслеживать версии ресурсов и удалять устаревшие файлы без вмешательства пользователя.


Автоматизация обновлений при сборке

При использовании sw-precache через Gulp или Grunt можно интегрировать генерацию Service Worker в процесс сборки, обеспечивая автоматическое обновление при каждом деплое:

swPrecache.write('service-worker.js', {
  staticFileGlobs: [
    'index.html',
    'css/**/*.css',
    'js/**/*.js',
    'images/**/*.{png,jpg,gif,svg}'
  ],
  stripPrefix: '/',
  navigateFallback: '/index.html',
  verbose: true,
});

Каждое изменение файлов в staticFileGlobs изменяет хеш, и браузер распознаёт новый Service Worker.


Рекомендации по безопасному обновлению

  • Использовать skipWaiting() только после тщательного тестирования, чтобы избежать конфликтов при одновременном использовании старых и новых ресурсов.
  • Всегда удалять устаревшие кэши, чтобы не накапливать лишние данные.
  • Интегрировать обновление Service Worker с системой версионирования приложения для точного контроля версий.
  • Тестировать работу обновления на разных браузерах, особенно на мобильных устройствах, где поведение Service Worker может отличаться.

Эта стратегия позволяет обеспечить мгновенное внедрение новых версий приложения, минимизировать вероятность использования устаревших кэшей и поддерживать актуальность контента без вмешательства пользователя.