When you deploy a new Service Worker, it goes through a specific lifecycle:
// Detect Service Worker updates
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js').then(registration => {
// Check for updates every minute
setInterval(() => {
registration.update();
}, 60 * 1000);
// Listen for new SW
registration.addEventListener('updatefound', () => {
const newWorker = registration.installing;
newWorker.addEventListener('statechange', () => {
if (newWorker.state === 'installed') {
if (navigator.serviceWorker.controller) {
// New version available!
showUpdateNotification();
}
}
});
});
});
}
// sw.js
self.addEventListener('install', () => {
self.skipWaiting();
});
self.addEventListener('activate', (event) => {
event.waitUntil(self.clients.claim());
});
// page.js - Force update when user clicks "Update"
function applyUpdate() {
if (window.newWorker) {
window.newWorker.postMessage({ type: 'SKIP_WAITING' });
}
}
// sw.js
self.addEventListener('message', (event) => {
if (event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
});
// Show a notification bar
function showUpdateNotification() {
const banner = document.createElement('div');
banner.className = 'update-banner';
banner.innerHTML = `
A new version is available.
<button onclick="applyUpdate()">Update</button>
<button onclick="dismissUpdate()">Later</button>
`;
document.body.prepend(banner);
}
// Listen for controller change (after update)
let refreshing = false;
navigator.serviceWorker.addEventListener('controllerchange', () => {
if (refreshing) return;
refreshing = true;
window.location.reload();
});
// sw.js - Use cache versioning
const CACHE_VERSION = 'v2';
const STATIC_CACHE = `static-${CACHE_VERSION}`;
const DYNAMIC_CACHE = `dynamic-${CACHE_VERSION}`;
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then(keys => {
return Promise.all(
keys.map(key => {
if (key !== STATIC_CACHE && key !== DYNAMIC_CACHE) {
return caches.delete(key);
}
})
);
})
);
});
Progressive enhancement means your PWA should work as a basic website even without JavaScript, then enhance when more capabilities are available.
// Check feature availability
const features = {
serviceWorker: 'serviceWorker' in navigator,
cache: 'caches' in window,
indexedDB: 'indexedDB' in window,
notifications: 'Notification' in window,
pushManager: 'PushManager' in window,
sync: 'SyncManager' in window,
bluetooth: 'bluetooth' in navigator,
usb: 'usb' in navigator,
nfc: 'nfc' in navigator
};
// Progressively enhance based on available features
if (features.serviceWorker) {
// Register SW for offline support
}
if (features.notifications) {
// Enable push notifications
}
if (features.sync) {
// Enable background sync
}