Service Worker (Сервис-воркер) — это скрипт, который работает в фоновом режиме браузера и позволяет реализовать возможности PWA (оффлайн-работу, push-уведомления, фоновую синхронизацию).
CRA уже включает базовую поддержку Service Worker:
// В файле src/index.js
import * as serviceWorkerRegistration from './serviceWorkerRegistration';
// Регистрация сервис-воркера
serviceWorkerRegistration.register();
Для кастомизации измените src/service-worker.js
:
// Пример кеширования
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open('my-cache-v1').then((cache) => {
return cache.addAll([
'/',
'/index.html',
'/static/js/main.chunk.js',
'/static/css/main.css'
]);
})
);
});
public/sw.js
:
const CACHE_NAME = 'my-app-cache-v1';
const urlsToCache = [
'/',
'/index.html',
'/manifest.json',
'/static/media/logo.123456.png'
];
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => cache.addAll(urlsToCache))
});
src/registerServiceWorker.js
:
export function register() {
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js')
.then(registration => {
console.log('SW registered:', registration);
})
.catch(error => {
console.log('SW registration failed:', error);
});
});
}
}
src/index.js
:
import { register } from './registerServiceWorker';
register();
Workbox — это библиотека от Google для работы с Service Workers:
npm install workbox-webpack-plugin --save-dev
webpack.config.js
:
const { InjectManifest } = require('workbox-webpack-plugin');
module.exports = {
plugins: [
new InjectManifest({
swSrc: './src/sw.js',
swDest: 'service-worker.js',
})
]
}
Пример src/sw.js
с Workbox:
import { precacheAndRoute } from 'workbox-precaching';
import { registerRoute } from 'workbox-routing';
import { CacheFirst } from 'workbox-strategies';
// Предварительное кеширование
precacheAndRoute(self.__WB_MANIFEST);
// Динамическое кеширование API
registerRoute(
new RegExp('/api/'),
new CacheFirst({
cacheName: 'api-cache'
})
);
registerRoute(
({request}) => request.destination === 'image',
new CacheFirst()
);
registerRoute(
({url}) => url.pathname.startsWith('/api'),
new NetworkFirst()
);
registerRoute(
({request}) => request.destination === 'style',
new StaleWhileRevalidate()
);
navigator.serviceWorker.getRegistrations().then(regs => {
regs.forEach(reg => reg.unregister());
caches.keys().then(keys => keys.forEach(key => caches.delete(key)));
});
const CACHE_VERSION = 'v2';
const CACHE_NAME = `my-app-${CACHE_VERSION}`;
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cache => {
if (cache !== CACHE_NAME) {
return caches.delete(cache);
}
})
);
})
);
});
self.addEventListener('fetch', event => {
if (event.request.url.includes('/lazy/')) {
event.respondWith(
caches.match(event.request).then(response => {
return response || fetch(event.request).then(fetchResponse => {
return caches.open(CACHE_NAME).then(cache => {
cache.put(event.request, fetchResponse.clone());
return fetchResponse;
});
});
})
);
}
});
добавление Service Worker в React приложение можно выполнить через CRA, вручную или с помощью Workbox. Выбор стратегии кеширования зависит от типа ресурсов. Для production-приложений рекомендуется использовать Workbox для более надежной работы и простого обновления кеша. Всегда тестируйте Service Worker в разных сценариях и не забывайте про версионирование кеша.