Workbox предоставляет удобный механизм для настройки поведения
сервис-воркеров при обработке сетевых запросов. Одним из ключевых
инструментов является возможность переопределения стандартного
fetch-обработчика, что позволяет полностью
контролировать логику получения ресурсов и управления кэшированием.
В Workbox fetch-обработчик используется для перехвата
всех запросов, инициированных страницей, и последующей работы с ними
через заданные стратегии (Cache First, Network First, Stale While
Revalidate и другие). По умолчанию Workbox автоматически регистрирует
обработчики для маршрутов через
workbox.routing.registerRoute.
Стандартная запись маршрута выглядит так:
import { registerRoute } from 'workbox-routing';
import { CacheFirst } from 'workbox-strategies';
registerRoute(
({request}) => request.destination === 'image',
new CacheFirst({
cacheName: 'images-cache',
})
);
В этом примере Workbox автоматически создает
fetch-обработчик для запросов изображений, применяя
стратегию CacheFirst.
Для полной кастомизации поведения можно создать собственный
fetch-обработчик, который будет обрабатывать все
запросы или только их часть. Это делается через
self.addEventListener('fetch', ...).
Пример переопределения стандартного поведения:
self.addEventListener('fetch', (event) => {
const { request } = event;
if (request.url.includes('/api/')) {
event.respondWith(
fetch(request)
.then((response) => {
// Дополнительная обработка ответа
const clonedResponse = response.clone();
caches.open('api-cache').then((cache) => {
cache.put(request, clonedResponse);
});
return response;
})
.catch(() => caches.match(request))
);
return;
}
// Для всех остальных запросов использовать стандартную стратегию Workbox
event.respondWith(workbox.strategies.networkFirst().handle({ request }));
});
Ключевые моменты:
event.respondWith() перехватывает ответ на запрос и
позволяет вернуть кастомный объект Response.request.clone() обязательно при записи
ответа в кэш, чтобы не истощить поток..handle({ request }).setCatchHandler для глобального управления ошибкамиWorkbox предоставляет метод
workbox.routing.setCatchHandler, который позволяет задать
глобальный обработчик ошибок для всех маршрутов. Это особенно полезно
при переопределении fetch-логики:
import { setCatchHandler } from 'workbox-routing';
setCatchHandler(async ({ event }) => {
if (event.request.destination === 'document') {
return caches.match('/offline.html');
}
return Response.error();
});
Пояснения:
event.request.destination позволяет определить тип
ресурса (document, script, image и т.д.).Response.error() используется для ресурсов,
которые не удалось получить ни из сети, ни из кэша.Для сложных сценариев часто используют комбинацию стандартных маршрутов и переопределенного fetch. Например, можно задать специальные правила для API-запросов и оставить изображения под управлением стандартной стратегии:
import { registerRoute } from 'workbox-routing';
import { CacheFirst } from 'workbox-strategies';
// Стандартный маршрут для изображений
registerRoute(
({request}) => request.destination === 'image',
new CacheFirst({
cacheName: 'images-cache',
})
);
// Кастомный fetch для API
self.addEventListener('fetch', (event) => {
if (event.request.url.includes('/api/')) {
event.respondWith(customApiHandler(event.request));
}
});
async function customApiHandler(request) {
try {
const response = await fetch(request);
const clone = response.clone();
const cache = await caches.open('api-cache');
await cache.put(request, clone);
return response;
} catch {
return caches.match(request) || new Response(null, { status: 503 });
}
}
При переопределении fetch важно учитывать следующие нюансы:
clone().async function customApiHandler(request) {
const response = await fetch(request);
if (response.ok) {
const clone = response.clone();
const cache = await caches.open('api-cache');
await cache.put(request, clone);
}
return response;
}
self.addEventListener('fetch', (event) => {
if (event.request.destination === 'image') {
event.respondWith(
fetch(event.request).catch(() => caches.match('/images/fallback.png'))
);
}
});
self.addEventListener('fetch', (event) => {
if (event.request.url.includes('/news/')) {
event.respondWith(workbox.strategies.staleWhileRevalidate().handle({ request: event.request }));
} else if (event.request.url.includes('/static/')) {
event.respondWith(workbox.strategies.cacheFirst().handle({ request: event.request }));
}
});
Эти подходы показывают, что переопределение
fetch-обработчика в Workbox обеспечивает полную гибкость и
контроль над сетевыми запросами, сохраняя при этом возможности
встроенных стратегий кэширования и управления офлайн-доступом.