Workbox в многостраничных приложениях

Workbox — это библиотека от Google, предназначенная для упрощения работы с сервис-воркерами и реализации стратегий кэширования в веб-приложениях. В многостраничных приложениях (MPA) её использование требует специфического подхода, так как каждая страница может иметь собственные скрипты, стили и ресурсы. Основная цель — улучшение производительности и оффлайн-доступности без значительного изменения существующей архитектуры.


Регистрация сервис-воркера

Сервис-воркер регистрируется в основном JavaScript-файле каждой страницы или в общем скрипте, который подключается ко всем страницам:

if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/sw.js').then(registration => {
      console.log('Service Worker зарегистрирован с областью:', registration.scope);
    }).catch(error => {
      console.log('Регистрация Service Worker не удалась:', error);
    });
  });
}

Ключевое значение здесь имеет область регистрации (scope), которая определяет, какие URL будут обслуживаться сервис-воркером. В MPA часто используется корневая область ('/') для покрытия всех страниц.


Создание сервис-воркера с Workbox

Workbox предоставляет модуль workbox-core для базовой настройки и workbox-precaching, workbox-routing, workbox-strategies для кэширования ресурсов и маршрутизации запросов.

Пример sw.js:

import {registerRoute} from 'workbox-routing';
import {StaleWhileRevalidate} from 'workbox-strategies';
import {precacheAndRoute} from 'workbox-precaching';

// Пресейвинг статических файлов
precacheAndRoute(self.__WB_MANIFEST);

// Маршрутизация всех CSS и JS с стратегией StaleWhileRevalidate
registerRoute(
  ({request}) => request.destination === 'style' || request.destination === 'script',
  new StaleWhileRevalidate({
    cacheName: 'static-resources',
  })
);

// Кэширование изображений с лимитом на 60 элементов
registerRoute(
  ({request}) => request.destination === 'image',
  new StaleWhileRevalidate({
    cacheName: 'images',
    plugins: [
      new workbox.expiration.ExpirationPlugin({
        maxEntries: 60,
        maxAgeSeconds: 30 * 24 * 60 * 60, // 30 дней
      }),
    ],
  })
);

Пресейвинг HTML-страниц

В многостраничных приложениях важно предусмотреть кэширование HTML страниц. Workbox позволяет явно перечислить страницы для пресейвинга:

precacheAndRoute([
  {url: '/index.html', revision: '123456'},
  {url: '/about.html', revision: '123457'},
  {url: '/contact.html', revision: '123458'},
]);

Ключевой момент: каждая HTML-страница должна иметь уникальный revision или хэш, чтобы при обновлении контента старый кэш автоматически заменялся.


Стратегии кэширования

Workbox поддерживает несколько стратегий кэширования, которые выбираются в зависимости от типа ресурса:

  • Cache First — сначала ищет ресурс в кэше, затем в сети. Подходит для редко обновляемых файлов (изображения, иконы).
  • Network First — сначала обращается к сети, при недоступности — к кэшу. Используется для HTML страниц и API.
  • Stale While Revalidate — отдаёт кэш, но одновременно обновляет его из сети. Идеальна для CSS, JS, изображений.

Пример Network First для страниц:

registerRoute(
  ({request}) => request.destination === 'document',
  new workbox.strategies.NetworkFirst({
    cacheName: 'html-pages',
    plugins: [
      new workbox.expiration.ExpirationPlugin({
        maxEntries: 50,
        maxAgeSeconds: 7 * 24 * 60 * 60, // 7 дней
      }),
    ],
  })
);

Работа с версиями и обновлениями

MPA часто обновляются фрагментарно. Чтобы сервис-воркер корректно обновлял кэш:

  1. Каждая страница и ресурс должны иметь revision или хэш.
  2. При изменении ресурсов необходимо пересоздавать манифест (__WB_MANIFEST) с актуальными ревизиями.
  3. Использовать плагин workbox-core.skipWaiting() для немедленной активации нового сервис-воркера.
import {skipWaiting, clientsClaim} from 'workbox-core';

skipWaiting();
clientsClaim();

Эти вызовы позволяют новому сервис-воркеру сразу управлять всеми страницами и обновлять устаревшие кэши.


Кэширование динамических API-запросов

Для API-запросов используют стратегию Network First с ограничением по количеству элементов и сроку жизни:

registerRoute(
  ({url}) => url.pathname.startsWith('/api/'),
  new workbox.strategies.NetworkFirst({
    cacheName: 'api-cache',
    networkTimeoutSeconds: 5,
    plugins: [
      new workbox.expiration.ExpirationPlugin({
        maxEntries: 30,
        maxAgeSeconds: 60 * 60, // 1 час
      }),
    ],
  })
);

Особенность MPA: API-запросы могут использоваться на нескольких страницах, поэтому важно унифицировать их кэш.


Lazy-loading и кэширование отдельных страниц

Workbox позволяет кэшировать страницы и ресурсы по мере их посещения:

registerRoute(
  ({request}) => request.destination === 'document',
  new workbox.strategies.StaleWhileRevalidate({
    cacheName: 'dynamic-pages',
  })
);

Эта стратегия подходит для страниц, которые не входят в пресейвинг, но часто посещаются пользователями.


Логирование и отладка

Для контроля работы сервис-воркера и стратегий Workbox можно включить логирование:

import {setConfig} from 'workbox-core';

setConfig({debug: true});

Это позволит отслеживать, какие ресурсы кэшируются, обновляются или извлекаются из сети.