Service Worker Overview: Concepts, Caching Strategies, Lifecycle, and Practical Implementation
This article introduces Service Workers as the core technology of progressive web apps, explains their advantages, security constraints, lifecycle events, CacheStorage API usage, various caching strategies, version‑controlled caching with webpack, background synchronization, and demonstrates how to pass data using IndexedDB, all illustrated with complete code examples.
Service Worker is the core technology of progressive web applications (PWA) that runs independently in the background after registration, intercepts network requests, and can operate even when the browser is closed.
Advantages of Service Worker
Supports offline access by caching static resources on first visit.
Speeds up loading because cached resources are served without network latency.
Provides reliable functionality in offline or unstable network conditions.
Security Policy
Service Workers can only be registered on pages served over HTTPS or on localhost . The scope of a Service Worker is limited to the directory where its script resides, and can be narrowed with the scope option during registration.
How to Use
Register a Service Worker in the browser environment:
window.onload = function () {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('./serviceworker.js', { scope: '/' });
}
};The Service Worker script typically listens to install and fetch events. Example:
self.addEventListener('install', function (event) {
event.waitUntil(
caches.open('cache')
.then(cache => cache.addAll(['./offline.html']))
);
});
self.addEventListener('fetch', function (event) {
event.respondWith(
fetch(event.request).catch(() => caches.match('./offline.html'))
);
});Lifecycle
After registration the Service Worker enters the installing phase, runs the install event, then moves to activated where the activate event runs. When idle it can handle fetch , sync , and message events. If a newer worker is installed while an older one controls pages, the new worker waits in the waiting state until all controlled pages are closed.
CacheStorage API
CacheStorage provides promise‑based methods such as caches.open(cacheName) , caches.keys() , cache.addAll(urls) , cache.put(request, response) , cache.delete(key) , and cache.match(request) to manage cached assets.
Caching Strategies
Three typical strategies are demonstrated:
Cache‑first: serve from cache, fall back to network.
Network‑first: try network, cache the response for future use.
Stale‑while‑revalidate: return cached response immediately and update the cache in the background.
Version‑Controlled Caching
Using webpack, the Service Worker injects a version string from package.json and a list of assets. During install it caches all assets under a versioned cache key, and during activate it removes old caches:
self.addEventListener('activate', function (event) {
event.waitUntil(
caches.keys().then(keys => Promise.all(
keys.filter(key => key !== cacheKey).map(key => caches.delete(key))
)).then(() => self.clients.claim())
);
});Background Sync
Register a sync tag after a user action so that failed requests are retried when the network is restored:
navigator.serviceWorker.ready.then(reg => {
document.getElementById('submit').addEventListener('click', () => {
reg.sync.register('send-messages');
});
});
self.addEventListener('sync', event => {
if (event.tag === 'send-messages') {
event.waitUntil(sendMessages().catch(() => {
if (event.lastChance) console.log('No more retries');
return Promise.reject();
}));
}
});
function sendMessages() {
return fetch('http://localhost:3000/').then(r => r.json()).then(d => {
if (d.errCode === 0) return Promise.resolve();
return Promise.reject();
});
}Passing Data with IndexedDB
IndexedDB can be used to store parameters that the Service Worker later processes. A minimal wrapper is provided:
const _global = typeof window === 'undefined' ? self : window;
function openDataBase() {
return new Promise((resolve, reject) => {
const request = _global.indexedDB.open('content-db', 1);
request.onupgradeneeded = e => {
const db = e.target.result;
if (!db.objectStoreNames.contains('list')) {
db.createObjectStore('list', { keyPath: 'id', autoIncrement: true });
}
};
request.onerror = reject;
request.onsuccess = e => resolve(e.target.result);
});
}
async function openObjectStore(storeName, mode) {
const db = await openDataBase();
return db.transaction(storeName, mode).objectStore(storeName);
}
_global.db = {
async set(content) {
const store = await openObjectStore('list', 'readwrite');
return store.add({ content });
},
async getAll() {
const store = await openObjectStore('list');
return new Promise(resolve => {
const data = [];
store.openCursor().onsuccess = e => {
const cursor = e.target.result;
if (!cursor) return resolve(data);
data.push(cursor.value);
cursor.continue();
};
});
},
async clear() {
const store = await openObjectStore('list', 'readwrite');
return store.clear();
}
};In the page script, data is stored via db.set(content) and a sync tag is registered. The Service Worker retrieves all stored contents, sends them, and clears the store upon success.
Conclusion
The article covers the fundamentals of Service Workers, their caching capabilities, lifecycle management, versioned asset handling, background synchronization, and data passing through IndexedDB, providing a comprehensive guide for optimizing web applications.
360 Tech Engineering
Official tech channel of 360, building the most professional technology aggregation platform for the brand.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.