Implementing Offline Caching and Resource Optimization with Service Worker
Problem Description
A Service Worker is a script that runs independently in the browser background, acting as a network proxy capable of intercepting and handling network requests from web pages. Through Service Workers, we can achieve reliable offline caching, resource preloading, message push notifications, and more. Please explain in detail how to utilize Service Workers to optimize front-end resource loading performance and achieve offline availability.
Knowledge Explanation
-
Characteristics of Service Workers
- Runs independently of the main thread and does not block page rendering.
- Requires an HTTPS environment (localhost can be used for local development).
- Has a complete lifecycle (install → wait → activate).
- Can intercept network requests within its scope (including HTML, CSS, JS, images, etc.).
-
Core Implementation Steps
Step 1: Register the Service Worker
Detect browser support and complete registration in the main thread:if ('serviceWorker' in navigator) { // Note the scope: by default, it covers the directory where sw.js is located and its subdirectories. navigator.serviceWorker.register('/sw.js') .then(registration => { console.log('SW registration successful, scope:', registration.scope); }) .catch(error => { console.log('SW registration failed:', error); }); }Step 2: Cache Critical Resources During the Install Phase
Listen for the install event in sw.js and use the Cache API to store static resources:const CACHE_NAME = 'v1-static-cache'; const urlsToCache = [ '/', '/css/main.css', '/js/app.js', '/images/logo.png' ]; self.addEventListener('install', event => { // Force skipping the waiting phase and directly activate the new SW. self.skipWaiting(); event.waitUntil( caches.open(CACHE_NAME) .then(cache => { return cache.addAll(urlsToCache); // Atomic operation: all succeed or fail. }) ); });Step 3: Intercept Requests and Apply Caching Strategies
Implement various caching strategies through the fetch event (here, the "Cache First, Network Fallback" strategy is shown):self.addEventListener('fetch', event => { event.respondWith( caches.match(event.request) // Match cache .then(response => { if (response) { return response; // Return cached copy } // Clone the request (stream data can only be used once). const fetchRequest = event.request.clone(); return fetch(fetchRequest).then(response => { // Validate response effectiveness if (!response || response.status !== 200) { return response; } // Dynamically cache new resources (avoid caching cross-origin requests). if (event.request.url.startsWith(self.location.origin)) { const responseToCache = response.clone(); caches.open(CACHE_NAME) .then(cache => { cache.put(event.request, responseToCache); }); } return response; }); }) ); });Step 4: Version Updates and Cache Cleanup
Listen for the activate event to delete old caches:self.addEventListener('activate', event => { event.waitUntil( caches.keys().then(cacheNames => { return Promise.all( cacheNames.map(cacheName => { if (cacheName !== CACHE_NAME) { return caches.delete(cacheName); // Clean up old caches } }) ); }) ); }); -
Advanced Optimization Techniques
- Pre-caching Strategy: Cache all critical path resources during the install phase.
- Runtime Caching: Cache dynamic requests (e.g., use the "Network First" strategy for API data).
- Cache Capacity Management: Control cache size using the LRU (Least Recently Used) algorithm.
- Background Synchronization: Use Background Sync to implement offline operation queues.
-
Practical Considerations
- Avoid caching opaque responses from cross-origin requests (as they become unreadable).
- Use the "Network First" strategy for HTML documents to ensure content timeliness.
- Force a refresh of all caches by updating the CACHE_NAME.
- Clean up temporary resources in the unload event.
Summary
Service Workers store static resources locally through caching strategies, reducing network request latency and significantly improving performance for repeat visits. Combined with appropriate update mechanisms, they ensure both offline availability and access to the latest content. This technology is particularly suitable for PWA (Progressive Web App) scenarios and is a core technology for achieving native app-like experiences.