Service Worker 是一种特殊的 Web Worker,它作为网络代理运行在浏览器和服务器之间,提供离线功能、推送通知和后台同步等能力。
Service Worker 的核心概念
特点
- 独立于页面生命周期运行
- 拦截和处理网络请求
- 必须在 HTTPS 环境下运行(localhost 除外)
- 可以实现离线缓存和资源预加载
- 支持推送通知和后台同步
注册 Service Worker
javascript// 检查浏览器支持 if ('serviceWorker' in navigator) { // 注册 Service Worker navigator.serviceWorker.register('/service-worker.js') .then(function(registration) { console.log('Service Worker 注册成功:', registration.scope); }) .catch(function(error) { console.log('Service Worker 注册失败:', error); }); }
Service Worker 生命周期
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 延迟安装完成 event.waitUntil( caches.open(CACHE_NAME) .then(function(cache) { console.log('已打开缓存'); return cache.addAll(urlsToCache); }) ); // 立即激活新的 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) { // 删除旧版本的缓存 if (cacheName !== CACHE_NAME) { console.log('删除旧缓存:', cacheName); return caches.delete(cacheName); } }) ); }) ); // 立即控制所有客户端 self.clients.claim(); });
3. Fetch(拦截请求)
javascriptself.addEventListener('fetch', function(event) { event.respondWith( caches.match(event.request) .then(function(response) { // 缓存命中,返回缓存 if (response) { return response; } // 缓存未命中,发起网络请求 return fetch(event.request).then( function(response) { // 检查是否为有效响应 if (!response || response.status !== 200 || response.type !== 'basic') { return response; } // 克隆响应(响应流只能使用一次) const responseToCache = response.clone(); caches.open(CACHE_NAME) .then(function(cache) { cache.put(event.request, responseToCache); }); return response; } ); }) ); });
缓存策略
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; }) ); });
推送通知
订阅推送
javascript// 主线程 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('已接收推送订阅:', pushSubscription); return pushSubscription; }); }
处理推送消息
javascript// service-worker.js self.addEventListener('push', function(event) { const options = { body: event.data ? event.data.text() : '新消息', icon: '/images/icon.png', badge: '/images/badge.png', vibrate: [100, 50, 100], data: { dateOfArrival: Date.now(), primaryKey: 1 } }; event.waitUntil( self.registration.showNotification('推送通知', options) ); }); // 处理通知点击 self.addEventListener('notificationclick', function(event) { event.notification.close(); event.waitUntil( clients.openWindow('/') ); });
后台同步
注册同步事件
javascript// 主线程 navigator.serviceWorker.ready.then(function(registration) { registration.sync.register('sync-messages'); });
处理同步事件
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()) }); }
调试 Service Worker
Chrome DevTools
- 打开 Chrome DevTools
- 切换到 "Application" 面板
- 左侧选择 "Service Workers"
- 可以查看状态、更新、注销等操作
更新 Service Worker
javascript// 手动更新 navigator.serviceWorker.ready.then(function(registration) { registration.update(); }); // 监听更新 navigator.serviceWorker.addEventListener('controllerchange', function() { console.log('Service Worker 已更新'); window.location.reload(); });
最佳实践
- HTTPS 要求:生产环境必须使用 HTTPS
- 缓存版本管理:使用版本号管理缓存
- 错误处理:添加完善的错误处理
- 渐进增强:确保在不支持 Service Worker 的浏览器中也能正常工作
- 资源清理:及时清理旧版本的缓存
- 性能优化:合理选择缓存策略
- 安全性:验证请求来源,防止 CSRF 攻击