乐闻世界logo
搜索文章和话题

Service Worker 的生命周期是什么,如何实现离线缓存?

2月21日 15:24

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(激活)

javascript
self.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(拦截请求)

javascript
self.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(缓存优先)

javascript
self.addEventListener('fetch', function(event) { event.respondWith( caches.match(event.request) .then(function(response) { return response || fetch(event.request); }) ); });

2. Network First(网络优先)

javascript
self.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(仅网络)

javascript
self.addEventListener('fetch', function(event) { event.respondWith(fetch(event.request)); });

4. Cache Only(仅缓存)

javascript
self.addEventListener('fetch', function(event) { event.respondWith(caches.match(event.request)); });

5. Stale While Revalidate(缓存优先,后台更新)

javascript
self.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

  1. 打开 Chrome DevTools
  2. 切换到 "Application" 面板
  3. 左侧选择 "Service Workers"
  4. 可以查看状态、更新、注销等操作

更新 Service Worker

javascript
// 手动更新 navigator.serviceWorker.ready.then(function(registration) { registration.update(); }); // 监听更新 navigator.serviceWorker.addEventListener('controllerchange', function() { console.log('Service Worker 已更新'); window.location.reload(); });

最佳实践

  1. HTTPS 要求:生产环境必须使用 HTTPS
  2. 缓存版本管理:使用版本号管理缓存
  3. 错误处理:添加完善的错误处理
  4. 渐进增强:确保在不支持 Service Worker 的浏览器中也能正常工作
  5. 资源清理:及时清理旧版本的缓存
  6. 性能优化:合理选择缓存策略
  7. 安全性:验证请求来源,防止 CSRF 攻击
标签:Web Worker