Service Workers представляют собой отдельный слой JavaScript-логики, выполняемый браузером в фоне и перехватывающий сетевые запросы приложения. В связке с современными сборщиками, такими как Parcel, они становятся инструментом построения офлайн-режима, кэширования ресурсов, ускорения загрузки и реализации продвинутых стратегий доставки контента.
Service Workers работают независимо от основного потока выполнения страницы, не имеют прямого доступа к DOM и взаимодействуют с приложением через события и сообщения. Их ключевая роль — контроль сетевых запросов и управление кэшем.
Service Worker действует как промежуточный слой между браузером и сетью:
fetchВ контексте Parcel важно понимать, что сам Service Worker не «собирается» автоматически как обычный JS-файл приложения. Он требует отдельной конфигурации и часто отдельного входного файла, который затем обрабатывается сборщиком.
Parcel при этом отвечает за:
Базовая структура проекта обычно включает отдельный файл:
/src
index.js
service-worker.js
Регистрация Service Worker происходит в основном потоке приложения:
if ('serviceWorker' in navigator) {
window.addEventListener('load', async () => {
try {
const registration = await navigator.serviceWorker.register('/service-worker.js');
console.log('Service Worker зарегистрирован:', registration.scope);
} catch (err) {
console.error('Ошибка регистрации Service Worker:', err);
}
});
}
Parcel при сборке:
service-worker.js как отдельную точку
входаОдним из ключевых преимуществ использования Parcel является автоматическое хеширование файлов. Это критично для Service Worker, так как стратегия кэширования часто зависит от неизменяемых URL ресурсов.
Пример:
import logo from './logo.png';
self.addEventListener('install', event => {
event.waitUntil(
caches.open('app-cache-v1').then(cache => {
return cache.addAll([
'/',
logo
]);
})
);
});
После сборки Parcel преобразует logo.png в
оптимизированный файл с хешем:
logo.8f3a91c2.png
Это позволяет:
Service Worker проходит несколько стадий:
Parcel не изменяет сам жизненный цикл, но влияет на структуру файлов, которые участвуют в нём.
Типичная стратегия версионирования при использовании Parcel:
const CACHE_NAME = 'app-cache-v2';
Смена версии кэша часто синхронизируется с новым билдом Parcel.
Подходит для статических ресурсов:
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request).then(response => {
return response || fetch(event.request);
})
);
});
Parcel усиливает эту стратегию благодаря стабильным хешированным именам файлов.
Используется для динамического контента:
self.addEventListener('fetch', event => {
event.respondWith(
fetch(event.request).catch(() => caches.match(event.request))
);
});
Комбинированная стратегия:
self.addEventListener('fetch', event => {
event.respondWith(
caches.open('runtime-cache').then(async cache => {
const cached = await cache.match(event.request);
const networkPromise = fetch(event.request).then(response => {
cache.put(event.request, response.clone());
return response;
});
return cached || networkPromise;
})
);
});
Хотя Parcel может работать с Service Worker напрямую, на практике часто используется Workbox для генерации кэша и стратегий.
Parcel не навязывает архитектуру, но позволяет подключать Workbox как зависимость:
npm install workbox-window
В Service Worker:
import { precacheAndRoute } from 'workbox-precaching';
precacheAndRoute(self.__WB_MANIFEST);
Parcel может быть настроен на генерацию manifest-файла через плагины или дополнительные скрипты сборки.
В режиме разработки Service Worker ведёт себя иначе:
Типичный подход:
self.addEventListener('fetch', event => {
if (self.location.hostname === 'localhost') {
return;
}
});
Parcel dev-server часто использует HMR (Hot Module Replacement), но Service Worker не поддерживает HMR напрямую, поэтому требуется ручная перерегистрация.
Одна из частых проблем — «залипание» старой версии Service Worker.
Правильный подход:
self.addEventListener('install', event => {
self.skipWaiting();
});
self.addEventListener('activate', event => {
event.waitUntil(self.clients.claim());
});
Parcel здесь играет роль косвенно: изменение бандла автоматически приводит к новому URL файла, что запускает обновление Service Worker.
Parcel генерирует структуру вида:
dist/
index.html
index.4a7d2c.js
service-worker.91bc33.js
logo.8f3a91c2.png
Service Worker должен учитывать, что:
Типичные проблемы:
1. Кэширование index.html без обновления
Решение: использовать network-first стратегию для HTML.
2. Необновляемый Service Worker
Причина: браузер держит старую версию активной.
Решение: skipWaiting + clients.claim.
3. Конфликт путей после сборки Parcel
Причина: неправильный scope регистрации.
Решение:
navigator.serviceWorker.register('/service-worker.js', {
scope: '/'
});
4. Кэширование устаревших ассетов
Причина: отсутствие версии cache name.
Решение: привязка версии к сборке Parcel.
Parcel поддерживает ES-модули внутри Service Worker:
import { precache } from './cache-utils.js';
precache();
Это позволяет:
Комбинация Parcel и Service Worker даёт несколько уровней оптимизации:
Особенно эффективно работает с SPA, где Parcel создаёт единый бандл, а Service Worker обеспечивает его мгновенную доставку из кэша.
Service Worker работает только в защищённом контексте:
Parcel dev-server автоматически работает на localhost,
что упрощает тестирование, но production-сборка требует
HTTPS-хостинга.
Дополнительные ограничения:
Процесс обновления обычно выглядит так:
Эта модель делает обновления предсказуемыми и безопасными при правильной конфигурации кэша.