Service Worker Update Mechanism Explained
The Service Worker update mechanism is one of its important features, and understanding it is crucial for maintaining application stability and user experience.
Update Trigger Conditions
When the browser detects changes in the Service Worker file (byte difference), it triggers the update process.
javascript// Browser automatically checks for updates // The following situations trigger checks: // 1. When user visits the page // 2. When calling registration.update() // 3. When page refreshes (under certain conditions)
Update Lifecycle
1. Detect Update
javascript// Manually trigger update check navigator.serviceWorker.register('/sw.js').then(registration => { // Check for updates registration.update(); // Listen for update discovery registration.addEventListener('updatefound', () => { const newWorker = registration.installing; console.log('New Service Worker version found'); }); });
2. New Version Installation
javascript// sw.js const CACHE_NAME = 'app-v2'; // Update cache version self.addEventListener('install', event => { event.waitUntil( caches.open(CACHE_NAME).then(cache => { return cache.addAll([ '/', '/index.html', '/app.js', '/styles.css' ]); }) ); // Activate immediately, skip waiting phase self.skipWaiting(); });
3. Waiting Phase
By default, the new Service Worker version enters a waiting state until all pages controlled by the old version are closed.
javascript// Main thread listens for state changes navigator.serviceWorker.register('/sw.js').then(registration => { registration.addEventListener('updatefound', () => { const newWorker = registration.installing; newWorker.addEventListener('statechange', () => { switch (newWorker.state) { case 'installed': if (navigator.serviceWorker.controller) { // New version waiting for activation console.log('New version installed, waiting for activation'); showUpdateNotification(newWorker); } break; case 'activated': console.log('New version activated'); break; } }); }); });
4. Activation Phase
javascript// sw.js self.addEventListener('activate', event => { event.waitUntil( // Clean up old caches caches.keys().then(cacheNames => { return Promise.all( cacheNames .filter(name => name !== CACHE_NAME) .map(name => { console.log('Deleting old cache:', name); return caches.delete(name); }) ); }) ); // Take control of all pages immediately self.clients.claim(); });
User Notification for Updates
Option 1: Immediate Update (Recommended for Development)
javascript// Main thread let newWorker = null; navigator.serviceWorker.register('/sw.js').then(registration => { registration.addEventListener('updatefound', () => { newWorker = registration.installing; newWorker.addEventListener('statechange', () => { if (newWorker.state === 'installed' && navigator.serviceWorker.controller) { // Show update notification showUpdateBar(); } }); }); }); // User clicks update button function applyUpdate() { if (newWorker) { newWorker.postMessage({ action: 'skipWaiting' }); } } // Listen in sw.js self.addEventListener('message', event => { if (event.data.action === 'skipWaiting') { self.skipWaiting(); } });
Option 2: Graceful Update (Recommended for Production)
javascript// Listen for controller changes navigator.serviceWorker.addEventListener('controllerchange', () => { // Service Worker has switched, suggest user refresh page window.location.reload(); });
Option 3: Periodic Automatic Update
javascript// Check for updates every 60 minutes setInterval(() => { navigator.serviceWorker.ready.then(registration => { registration.update(); }); }, 60 * 60 * 1000);
Update Notification UI Example
javascriptfunction showUpdateBar() { const updateBar = document.createElement('div'); updateBar.className = 'update-bar'; updateBar.innerHTML = ` <span>New version found, update now?</span> <button id="update-btn">Update Now</button> <button id="dismiss-btn">Later</button> `; document.body.appendChild(updateBar); document.getElementById('update-btn').addEventListener('click', () => { applyUpdate(); updateBar.remove(); }); document.getElementById('dismiss-btn').addEventListener('click', () => { updateBar.remove(); }); }
Best Practices
1. Version Control
javascript// Use version numbers for cache management const CACHE_VERSION = 'v2.1.0'; const CACHE_NAME = `app-cache-${CACHE_VERSION}`;
2. Incremental Updates
javascript// Only update changed resources const urlsToCache = [ '/', '/index.html?v=2', // Add version number '/app.js?v=2.1', '/styles.css?v=2.1' ];
3. Update Strategy Selection
| Strategy | Use Case | User Experience |
|---|---|---|
| Immediate | Development, urgent fixes | Force refresh |
| Graceful | Production | User controlled |
| Silent | Non-critical updates | Unnoticeable |
4. Avoid Update Pitfalls
javascript// ❌ Wrong: Cache Service Worker itself // Service Worker file should not be cached // ✅ Correct: Ensure Service Worker file is not cached // Set in server configuration: // Cache-Control: no-cache, no-store, must-revalidate
Debugging Tips
javascript// View current Service Worker status navigator.serviceWorker.ready.then(registration => { console.log('Active:', registration.active); console.log('Installing:', registration.installing); console.log('Waiting:', registration.waiting); }); // Force update navigator.serviceWorker.getRegistration().then(reg => { reg.update(); }); // Unregister Service Worker navigator.serviceWorker.getRegistration().then(reg => { reg.unregister(); });
Summary
- Auto Detection: Browser automatically detects Service Worker file changes
- Install Wait: New version enters waiting state after installation by default
- Activate Takeover: New version activates after old pages close
- Cache Cleanup: Clean old version caches during activate phase
- User Notification: Provide friendly update notification mechanism