Service Worker is a special type of Web Worker that runs as a network proxy between the browser and server, providing offline capabilities, push notifications, and background synchronization.
Core Concepts of Service Worker
Characteristics
- Runs independently of page lifecycle
- Intercepts and handles network requests
- Must run in HTTPS environment (localhost is an exception)
- Can implement offline caching and resource preloading
- Supports push notifications and background synchronization
Registering Service Worker
javascript// Check browser support if ('serviceWorker' in navigator) { // Register Service Worker navigator.serviceWorker.register('/service-worker.js') .then(function(registration) { console.log('Service Worker registered successfully:', registration.scope); }) .catch(function(error) { console.log('Service Worker registration failed:', error); }); }
Service Worker Lifecycle
1. Install
javascript// service-worker.js const CACHE_NAME = 'my-cache-v1'; const urlsToCache = [ '/', '/styles/main.css', '/script/main.js', '/images/logo.png' ]; self.addEventListener('install', function(event) { // event.waitUntil delays installation completion event.waitUntil( caches.open(CACHE_NAME) .then(function(cache) { console.log('Cache opened'); return cache.addAll(urlsToCache); }) ); // Immediately activate new Service Worker self.skipWaiting(); });
2. Activate
javascriptself.addEventListener('activate', function(event) { event.waitUntil( caches.keys().then(function(cacheNames) { return Promise.all( cacheNames.map(function(cacheName) { // Delete old version caches if (cacheName !== CACHE_NAME) { console.log('Deleting old cache:', cacheName); return caches.delete(cacheName); } }) ); }) ); // Immediately control all clients self.clients.claim(); });
3. Fetch
javascriptself.addEventListener('fetch', function(event) { event.respondWith( caches.match(event.request) .then(function(response) { // Cache hit, return cache if (response) { return response; } // Cache miss, make network request return fetch(event.request).then( function(response) { // Check if valid response if (!response || response.status !== 200 || response.type !== 'basic') { return response; } // Clone response (response stream can only be used once) const responseToCache = response.clone(); caches.open(CACHE_NAME) .then(function(cache) { cache.put(event.request, responseToCache); }); return response; } ); }) ); });
Caching Strategies
1. Cache First
javascriptself.addEventListener('fetch', function(event) { event.respondWith( caches.match(event.request) .then(function(response) { return response || fetch(event.request); }) ); });
2. Network First
javascriptself.addEventListener('fetch', function(event) { event.respondWith( fetch(event.request) .then(function(response) { const responseToCache = response.clone(); caches.open(CACHE_NAME).then(function(cache) { cache.put(event.request, responseToCache); }); return response; }) .catch(function() { return caches.match(event.request); }) ); });
3. Network Only
javascriptself.addEventListener('fetch', function(event) { event.respondWith(fetch(event.request)); });
4. Cache Only
javascriptself.addEventListener('fetch', function(event) { event.respondWith(caches.match(event.request)); });
5. Stale While Revalidate
javascriptself.addEventListener('fetch', function(event) { event.respondWith( caches.match(event.request).then(function(cachedResponse) { const fetchPromise = fetch(event.request).then(function(networkResponse) { caches.open(CACHE_NAME).then(function(cache) { cache.put(event.request, networkResponse.clone()); }); return networkResponse; }); return cachedResponse || fetchPromise; }) ); });
Push Notifications
Subscribe to Push
javascript// Main thread function subscribeUser() { return navigator.serviceWorker.register('/service-worker.js') .then(function(registration) { const subscribeOptions = { userVisibleOnly: true, applicationServerKey: urlBase64ToUint8Array( 'BEl62iUYgUivxIkv69yViEuiBIa-IbRMhKDbjjVdMlzQJd0_...' ) }; return registration.pushManager.subscribe(subscribeOptions); }) .then(function(pushSubscription) { console.log('Received push subscription:', pushSubscription); return pushSubscription; }); }
Handle Push Messages
javascript// service-worker.js self.addEventListener('push', function(event) { const options = { body: event.data ? event.data.text() : 'New message', icon: '/images/icon.png', badge: '/images/badge.png', vibrate: [100, 50, 100], data: { dateOfArrival: Date.now(), primaryKey: 1 } }; event.waitUntil( self.registration.showNotification('Push Notification', options) ); }); // Handle notification click self.addEventListener('notificationclick', function(event) { event.notification.close(); event.waitUntil( clients.openWindow('/') ); });
Background Sync
Register Sync Event
javascript// Main thread navigator.serviceWorker.ready.then(function(registration) { registration.sync.register('sync-messages'); });
Handle Sync Event
javascript// service-worker.js self.addEventListener('sync', function(event) { if (event.tag === 'sync-messages') { event.waitUntil(syncMessages()); } }); function syncMessages() { return fetch('/api/sync-messages', { method: 'POST', body: JSON.stringify(getPendingMessages()) }); }
Debugging Service Worker
Chrome DevTools
- Open Chrome DevTools
- Switch to "Application" panel
- Select "Service Workers" on the left
- Can view status, update, unregister, etc.
Update Service Worker
javascript// Manual update navigator.serviceWorker.ready.then(function(registration) { registration.update(); }); // Listen for updates navigator.serviceWorker.addEventListener('controllerchange', function() { console.log('Service Worker updated'); window.location.reload(); });
Best Practices
- HTTPS Requirement: Must use HTTPS in production environment
- Cache Version Management: Use version numbers to manage caches
- Error Handling: Add comprehensive error handling
- Progressive Enhancement: Ensure normal operation in browsers without Service Worker support
- Resource Cleanup: Clean up old version caches in time
- Performance Optimization: Choose caching strategies appropriately
- Security: Verify request sources to prevent CSRF attacks