Workbox и Next.js

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

Next.js по умолчанию поддерживает серверный рендеринг (SSR) и статическую генерацию (SSG), что накладывает свои особенности на работу сервис-воркеров. Для корректного кэширования необходимо учитывать структуру сборки, динамические маршруты и публичные файлы.

Установка и настройка Workbox

Установка Workbox в проект на Next.js производится через npm:

npm install workbox-webpack-plugin --save-dev

или через yarn:

yarn add workbox-webpack-plugin --dev

Workbox интегрируется с Webpack через плагин GenerateSW или InjectManifest. В Next.js для этого используется модификация конфигурации Webpack через next.config.js:

const { GenerateSW } = require('workbox-webpack-plugin');

module.exports = {
  webpack: (config, { isServer }) => {
    if (!isServer) {
      config.plugins.push(
        new GenerateSW({
          clientsClaim: true,
          skipWaiting: true,
          runtimeCaching: [
            {
              urlPattern: /\.(?:png|jpg|jpeg|svg|gif)$/,
              handler: 'CacheFirst',
              options: {
                cacheName: 'images',
                expiration: { maxEntries: 60, maxAgeSeconds: 30 * 24 * 60 * 60 },
              },
            },
            {
              urlPattern: /^https:\/\/api\.example\.com\/.*$/,
              handler: 'NetworkFirst',
              options: {
                cacheName: 'api',
                networkTimeoutSeconds: 10,
                expiration: { maxEntries: 50, maxAgeSeconds: 5 * 60 },
              },
            },
          ],
        })
      );
    }
    return config;
  },
};

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

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

  • CacheFirst — сначала ищет ресурс в кэше, если его нет — делает сетевой запрос. Идеально подходит для статических ресурсов (изображения, шрифты, файлы JS/CSS).
  • NetworkFirst — сначала делает сетевой запрос, если сеть недоступна — возвращает кэшированную версию. Используется для API и динамических страниц.
  • StaleWhileRevalidate — возвращает кэшированную версию сразу и обновляет её в фоне через сетевой запрос. Подходит для данных, где не критично мгновенное обновление, но важно быстрое отображение контента.
  • NetworkOnly и CacheOnly — строгие стратегии для случаев, когда требуется полностью контролировать источник данных.

Кэширование страниц Next.js

Next.js поддерживает SSR и SSG, поэтому страницы генерируются на сервере и имеют динамические URL. Для работы Workbox с динамическими маршрутами рекомендуется использовать регулярные выражения в runtimeCaching:

{
  urlPattern: /^\/blog\/.*$/,
  handler: 'NetworkFirst',
  options: {
    cacheName: 'blog-pages',
    networkTimeoutSeconds: 5,
    expiration: { maxEntries: 100, maxAgeSeconds: 24 * 60 * 60 },
  },
}

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

Работа с публичными файлами и сборкой

Все файлы из папки public автоматически становятся доступными по URL и могут кэшироваться через Workbox. Для этого достаточно задать соответствующий urlPattern:

{
  urlPattern: /^\/_next\/static\/.*/,
  handler: 'CacheFirst',
  options: {
    cacheName: 'next-static',
    expiration: { maxEntries: 100, maxAgeSeconds: 7 * 24 * 60 * 60 },
  },
}

Важно учитывать версионирование Next.js (/_next/static/), чтобы кэш автоматически обновлялся после деплоя новой сборки.

Использование InjectManifest

Для более гибкого управления сервис-воркером применяется InjectManifest. В этом режиме создается кастомный сервис-воркер с вашей логикой, в который Workbox внедряет стратегию кэширования.

Пример настройки next.config.js:

const { InjectManifest } = require('workbox-webpack-plugin');
const path = require('path');

module.exports = {
  webpack: (config, { isServer }) => {
    if (!isServer) {
      config.plugins.push(
        new InjectManifest({
          swSrc: path.join(__dirname, 'src', 'service-worker.js'),
          swDest: 'service-worker.js',
          maximumFileSizeToCacheInBytes: 5 * 1024 * 1024,
        })
      );
    }
    return config;
  },
};

В src/service-worker.js можно определить кастомные стратегии:

import { precacheAndRoute } from 'workbox-precaching';
import { registerRoute } from 'workbox-routing';
import { NetworkFirst, CacheFirst } from 'workbox-strategies';

precacheAndRoute(self.__WB_MANIFEST);

registerRoute(
  ({ request }) => request.destination === 'image',
  new CacheFirst({ cacheName: 'images' })
);

registerRoute(
  ({ url }) => url.pathname.startsWith('/api/'),
  new NetworkFirst({ cacheName: 'api' })
);

Интеграция с Next.js API

API маршруты Next.js можно кэшировать через стратегию NetworkFirst с небольшим временем жизни кэша. Для сложных сценариев, где данные чувствительны к времени, используется StaleWhileRevalidate с коротким TTL.

Управление версиями кэша

Workbox автоматически добавляет хэш к файлам при сборке (precacheManifest). Для ручного контроля версий важно:

  • Обновлять кэш-имена при изменении логики или формата данных.
  • Использовать skipWaiting() и clientsClaim() для немедленного обновления сервис-воркера после деплоя.
self.addEventListener('activate', event => {
  event.waitUntil(
    caches.keys().then(keys =>
      Promise.all(keys.map(key => caches.delete(key)))
    )
  );
});

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