Проектирование офлайн-first приложения

Workbox — это библиотека для создания сервис-воркеров в приложениях на JavaScript, которая упрощает управление кэшированием ресурсов и стратегиями сетевых запросов. Центральная идея офлайн-first приложений заключается в том, чтобы пользователь мог продолжать работу с приложением даже при отсутствии сетевого соединения, а синхронизация с сервером происходила по мере возможности.

Сервис-воркер в Workbox регистрируется через workbox-window или напрямую через стандартный API navigator.serviceWorker.register(). После регистрации воркер способен перехватывать сетевые запросы и решать, каким образом реагировать: отдавать кэшированную версию, загружать с сети или комбинировать подходы.


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

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

  1. CacheFirst Используется для ресурсов, которые редко меняются, например, статические файлы CSS и JS. При запросе сначала проверяется кэш, и только при отсутствии ресурса происходит сетевой запрос.

    workbox.routing.registerRoute(
      ({request}) => request.destination === 'script' || request.destination === 'style',
      new workbox.strategies.CacheFirst({
        cacheName: 'static-resources',
        plugins: [
          new workbox.expiration.ExpirationPlugin({
            maxEntries: 50,
            maxAgeSeconds: 30 * 24 * 60 * 60, // 30 дней
          }),
        ],
      })
    );
  2. NetworkFirst Подходит для динамического контента, где актуальность данных критична, например, API-запросы. Вначале пробуется сеть, а при ошибке (например, offline) отдаётся кэш.

    workbox.routing.registerRoute(
      ({url}) => url.pathname.startsWith('/api/'),
      new workbox.strategies.NetworkFirst({
        cacheName: 'api-cache',
        networkTimeoutSeconds: 5,
        plugins: [
          new workbox.expiration.ExpirationPlugin({
            maxEntries: 100,
            maxAgeSeconds: 5 * 60, // 5 минут
          }),
        ],
      })
    );
  3. StaleWhileRevalidate Используется для контента, где важна скорость отображения, но нужно иметь актуальную версию для следующего посещения. Сначала возвращается кэш, а сеть обновляет его в фоне.

    workbox.routing.registerRoute(
      ({request}) => request.destination === 'image',
      new workbox.strategies.StaleWhileRevalidate({
        cacheName: 'image-cache',
        plugins: [
          new workbox.expiration.ExpirationPlugin({
            maxEntries: 200,
            maxAgeSeconds: 7 * 24 * 60 * 60, // 7 дней
          }),
        ],
      })
    );

Префетчинг и предзагрузка ресурсов

Для офлайн-first приложений критически важно заранее кэшировать основные ресурсы. Workbox позволяет реализовать precache, который автоматически управляет версионированием и очисткой устаревших файлов:

workbox.precaching.precacheAndRoute([
  {url: '/index.html', revision: '123456'},
  {url: '/styles/main.css', revision: '123456'},
  {url: '/scripts/app.js', revision: '123456'},
]);

Каждый объект с revision гарантирует обновление кэша при изменении файла. Workbox автоматически удаляет старые версии, предотвращая переполнения хранилища.


Продвинутое управление кэшами

Workbox предоставляет плагины для контроля поведения кэша:

  • ExpirationPlugin — ограничение числа элементов и времени хранения.
  • CacheableResponsePlugin — фильтрует ответы для кэширования по HTTP-кодам и заголовкам.
  • BroadcastUpdatePlugin — уведомляет страницу о изменении кэшированного ресурса для динамического обновления интерфейса.

Пример использования нескольких плагинов одновременно:

workbox.routing.registerRoute(
  ({request}) => request.destination === 'image',
  new workbox.strategies.CacheFirst({
    cacheName: 'advanced-image-cache',
    plugins: [
      new workbox.expiration.ExpirationPlugin({
        maxEntries: 100,
        maxAgeSeconds: 30 * 24 * 60 * 60,
      }),
      new workbox.cacheableResponse.CacheableResponsePlugin({
        statuses: [0, 200],
      }),
      new workbox.broadcastUpdate.BroadcastUpdatePlugin(),
    ],
  })
);

Фоновые синхронизации

Для офлайн-first приложений критично сохранять действия пользователя и синхронизировать их при восстановлении сети. Workbox предоставляет механизм background sync:

const bgSyncPlugin = new workbox.backgroundSync.BackgroundSyncPlugin('myQueue', {
  maxRetentionTime: 24 * 60, // хранить 24 часа
});

workbox.routing.registerRoute(
  /\/api\/send/,
  new workbox.strategies.NetworkOnly({
    plugins: [bgSyncPlugin],
  }),
  'POST'
);

Все POST-запросы при отсутствии сети попадают в очередь и автоматически повторяются при восстановлении соединения.


Кэширование маршрутов и SPA

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

workbox.routing.registerRoute(
  ({request, url}) => request.mode === 'navigate',
  new workbox.strategies.NetworkFirst({
    cacheName: 'html-cache',
  })
);

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


Оптимизация и мониторинг

Workbox позволяет:

  • Ограничивать размер кэша и время хранения для предотвращения переполнения.
  • Логировать события кэширования и ошибок.
  • Настраивать отдельные кэши для различных типов ресурсов, что упрощает управление версиями и обновлениями.

С помощью этих инструментов достигается эффективная офлайн-работа, минимизация трафика и ускорение загрузки приложения.