Сервис-воркеры в браузерах по умолчанию следуют строгой модели обновления: новый сервис-воркер загружается в фоновом режиме, но не активируется до тех пор, пока не будут закрыты все вкладки, использующие старую версию. Это может создавать задержку между публикацией нового кода и его фактическим использованием пользователями. Для приложений с динамичным контентом или быстрыми изменениями важно обеспечить механизм принудительного обновления сервис-воркера.
Workbox предоставляет инструменты для управления жизненным циклом сервис-воркера и позволяет реализовать стратегию мгновенного перехода на новую версию.
skipWaiting() и
clients.claim()skipWaiting() — метод, который
заставляет сервис-воркер переходить в активное состояние сразу после
установки, минуя фазу ожидания:
import { skipWaiting, clientsClaim } from 'workbox-core';
skipWaiting();
clientsClaim();
Эти два метода часто используются вместе для обеспечения мгновенного перехода на новую версию приложения.
При использовании Workbox можно включить автоматическую интеграцию
методов обновления через модуль workbox-core:
import { precacheAndRoute } from 'workbox-precaching';
import { registerRoute } from 'workbox-routing';
import { StaleWhileRevalidate } from 'workbox-strategies';
import { skipWaiting, clientsClaim } from 'workbox-core';
// Активируем мгновенное обновление
skipWaiting();
clientsClaim();
// Прекэширование статических ресурсов
precacheAndRoute(self.__WB_MANIFEST);
// Кэширование динамических ресурсов
registerRoute(
({ request }) => request.destination === 'image',
new StaleWhileRevalidate({
cacheName: 'images-cache',
})
);
В данном примере новый сервис-воркер будет установлен и активирован немедленно, при этом статические и динамические ресурсы будут закэшированы согласно указанным стратегиям.
Даже с skipWaiting() и clientsClaim()
иногда требуется информировать пользователя о том, что доступна новая
версия. Для этого можно использовать события waiting и
controllerchange:
self.addEventListener('install', event => {
console.log('Сервис-воркер установлен');
});
self.addEventListener('activate', event => {
console.log('Сервис-воркер активирован');
});
self.addEventListener('message', event => {
if (event.data && event.data.type === 'SKIP_WAITING') {
skipWaiting();
}
});
На стороне клиента:
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/service-worker.js').then(registration => {
registration.addEventListener('updatefound', () => {
const newWorker = registration.installing;
newWorker.addEventListener('statechange', () => {
if (newWorker.state === 'installed') {
if (navigator.serviceWorker.controller) {
// Новая версия готова
console.log('Доступна новая версия приложения');
}
}
});
});
});
}
Такая схема позволяет сохранять контроль над процессом обновления и при необходимости инициировать его по событию пользователя (например, при нажатии кнопки “Обновить”).
При сборке приложения через Workbox CLI или webpack-плагин важно учитывать:
clientsClaim() и
skipWaiting() в кастомном сервис-воркере.skipWaiting().Пример конфигурации webpack-плагина
WorkboxWebpackPlugin.GenerateSW:
const { GenerateSW } = require('workbox-webpack-plugin');
module.exports = {
plugins: [
new GenerateSW({
clientsClaim: true,
skipWaiting: true,
runtimeCaching: [
{
urlPattern: /\.(?:png|jpg|jpeg|svg)$/,
handler: 'StaleWhileRevalidate',
options: {
cacheName: 'images-cache',
expiration: {
maxEntries: 50,
},
},
},
],
}),
],
};
skipWaiting() активирует воркер сразу, что может привести к
конфликтам с открытыми вкладками, особенно если приложение активно
взаимодействует с локальными данными.skipWaiting() и
clientsClaim() в сочетании с уведомлением пользователя о
доступной новой версии.Эти подходы позволяют полностью контролировать жизненный цикл сервис-воркера и обеспечивают оперативное распространение новых версий веб-приложений с использованием Workbox.