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

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


Основные точки взаимодействия плагинов

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

  1. requestWillFetch – вызывается перед отправкой запроса на сервер.
  2. cacheKeyWillBeUsed – позволяет изменить ключ кэша перед проверкой наличия ресурса в кэше.
  3. cacheWillUpdate – определяет, должен ли ответ сохраняться в кэш и каким образом.
  4. cachedResponseWillBeUsed – вызывается перед возвратом ответа из кэша.
  5. fetchDidSucceed – вызывается после успешного сетевого запроса.
  6. fetchDidFail – вызывается при ошибке сетевого запроса.

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


Последовательность вызова плагинов

1. requestWillFetch

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

workbox.routing.registerRoute(
  '/api/',
  new workbox.strategies.NetworkFirst({
    plugins: [pluginA, pluginB, pluginC]
  })
);

В этом примере:

  1. pluginA.requestWillFetch(request)
  2. pluginB.requestWillFetch(modifiedRequestA)
  3. pluginC.requestWillFetch(modifiedRequestB)

Если какой-либо плагин возвращает новый Request, он используется дальше.


2. cacheKeyWillBeUsed

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

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

const cacheKeyPlugin = {
  cacheKeyWillBeUsed: async ({request, mode}) => {
    if (request.url.includes('v2')) {
      return new Request(request.url + '?v=2');
    }
    return request;
  }
};

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


3. cacheWillUpdate

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

const statusPlugin = {
  cacheWillUpdate: async ({response}) => {
    return response.status === 200 ? response : null;
  }
};

Если один плагин вернёт null, последующие плагины могут его изменить или также вернуть null, что приведет к пропуску записи в кэш.


4. cachedResponseWillBeUsed

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

  • Плагины вызываются с конца массива к началу, что позволяет последним указанным плагинам при маршруте иметь приоритет при проверке или модификации кэшированного ответа.

Пример:

const logPlugin = {
  cachedResponseWillBeUsed: async ({cachedResponse}) => {
    console.log('Используется кэш:', cachedResponse);
    return cachedResponse;
  }
};

5. fetchDidSucceed и fetchDidFail

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

Рекомендации по применению нескольких плагинов

  1. Разделение ответственности — каждый плагин должен выполнять одну задачу (например, проверка статуса, добавление заголовков, логирование). Это упрощает прогнозирование поведения цепочки.
  2. Осознанный порядок — важно располагать плагины так, чтобы ключевые модификации запроса или кэша выполнялись раньше, а вспомогательные действия (логирование, аналитика) — после.
  3. Использование асинхронности — все хуки поддерживают промисы, что позволяет делать сложные проверки или обращения к внешним источникам перед применением изменений.
  4. Тестирование цепочек — сложные цепочки из нескольких плагинов могут иметь непредсказуемое поведение, поэтому желательно проверять каждый хук отдельно и в комбинации.

Пример сложной конфигурации

workbox.routing.registerRoute(
  ({url}) => url.pathname.startsWith('/api/'),
  new workbox.strategies.NetworkFirst({
    cacheName: 'api-cache',
    plugins: [
      {
        requestWillFetch: async ({request}) => {
          const modified = new Request(request, {
            headers: {...request.headers, 'X-Custom': '1'}
          });
          return modified;
        }
      },
      {
        cacheKeyWillBeUsed: async ({request}) => {
          return new Request(request.url + '?cache=true');
        }
      },
      {
        cacheWillUpdate: async ({response}) => {
          return response.status === 200 ? response : null;
        }
      },
      {
        cachedResponseWillBeUsed: async ({cachedResponse}) => {
          return cachedResponse;
        }
      }
    ]
  })
);

В этом примере:

  • Сначала модифицируются заголовки запроса (requestWillFetch),
  • Затем изменяется ключ кэша (cacheKeyWillBeUsed),
  • После этого фильтруется ответ для сохранения в кэш (cacheWillUpdate),
  • И только после извлечения из кэша может быть вызван cachedResponseWillBeUsed.

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