Service Worker 更新机制详解
Service Worker 的更新机制是其重要特性之一,理解它对于维护应用的稳定性和用户体验至关重要。
更新触发条件
当浏览器检测到 Service Worker 文件发生变化时(字节差异),会触发更新流程。
javascript// 浏览器会自动检查更新 // 以下情况会触发检查: // 1. 用户访问页面时 // 2. 调用 registration.update() // 3. 页面刷新(在特定条件下)
更新生命周期
1. 检测更新
javascript// 手动触发更新检查 navigator.serviceWorker.register('/sw.js').then(registration => { // 检查更新 registration.update(); // 监听更新发现 registration.addEventListener('updatefound', () => { const newWorker = registration.installing; console.log('发现新版本 Service Worker'); }); });
2. 新版本安装
javascript// sw.js const CACHE_NAME = 'app-v2'; // 更新缓存版本 self.addEventListener('install', event => { event.waitUntil( caches.open(CACHE_NAME).then(cache => { return cache.addAll([ '/', '/index.html', '/app.js', '/styles.css' ]); }) ); // 立即激活,跳过等待阶段 self.skipWaiting(); });
3. 等待阶段(Waiting)
默认情况下,新版本的 Service Worker 会进入等待状态,直到旧版本控制的所有页面关闭。
javascript// 主线程监听状态变化 navigator.serviceWorker.register('/sw.js').then(registration => { registration.addEventListener('updatefound', () => { const newWorker = registration.installing; newWorker.addEventListener('statechange', () => { switch (newWorker.state) { case 'installed': if (navigator.serviceWorker.controller) { // 有新版本等待激活 console.log('新版本已安装,等待激活'); showUpdateNotification(newWorker); } break; case 'activated': console.log('新版本已激活'); break; } }); }); });
4. 激活阶段
javascript// sw.js self.addEventListener('activate', event => { event.waitUntil( // 清理旧缓存 caches.keys().then(cacheNames => { return Promise.all( cacheNames .filter(name => name !== CACHE_NAME) .map(name => { console.log('删除旧缓存:', name); return caches.delete(name); }) ); }) ); // 立即接管所有页面 self.clients.claim(); });
用户提示更新
方案一:立即更新(推荐用于开发环境)
javascript// 主线程 let newWorker = null; navigator.serviceWorker.register('/sw.js').then(registration => { registration.addEventListener('updatefound', () => { newWorker = registration.installing; newWorker.addEventListener('statechange', () => { if (newWorker.state === 'installed' && navigator.serviceWorker.controller) { // 显示更新提示 showUpdateBar(); } }); }); }); // 用户点击更新按钮 function applyUpdate() { if (newWorker) { newWorker.postMessage({ action: 'skipWaiting' }); } } // sw.js 中监听 self.addEventListener('message', event => { if (event.data.action === 'skipWaiting') { self.skipWaiting(); } });
方案二:优雅更新(推荐用于生产环境)
javascript// 监听 controller 变化 navigator.serviceWorker.addEventListener('controllerchange', () => { // Service Worker 已切换,建议用户刷新页面 window.location.reload(); });
方案三:定期自动更新
javascript// 每 60 分钟检查一次更新 setInterval(() => { navigator.serviceWorker.ready.then(registration => { registration.update(); }); }, 60 * 60 * 1000);
更新通知 UI 示例
javascriptfunction showUpdateBar() { const updateBar = document.createElement('div'); updateBar.className = 'update-bar'; updateBar.innerHTML = ` <span>发现新版本,是否更新?</span> <button id="update-btn">立即更新</button> <button id="dismiss-btn">稍后</button> `; document.body.appendChild(updateBar); document.getElementById('update-btn').addEventListener('click', () => { applyUpdate(); updateBar.remove(); }); document.getElementById('dismiss-btn').addEventListener('click', () => { updateBar.remove(); }); }
最佳实践
1. 版本控制
javascript// 使用版本号管理缓存 const CACHE_VERSION = 'v2.1.0'; const CACHE_NAME = `app-cache-${CACHE_VERSION}`;
2. 增量更新
javascript// 只更新变化的资源 const urlsToCache = [ '/', '/index.html?v=2', // 添加版本号 '/app.js?v=2.1', '/styles.css?v=2.1' ];
3. 更新策略选择
| 策略 | 适用场景 | 用户体验 |
|---|---|---|
| 立即更新 | 开发环境、紧急修复 | 强制刷新 |
| 优雅更新 | 生产环境 | 用户可控 |
| 静默更新 | 非关键更新 | 无感知 |
4. 避免更新陷阱
javascript// ❌ 错误:缓存 Service Worker 本身 // Service Worker 文件不应该被缓存 // ✅ 正确:确保 Service Worker 文件不被缓存 // 在服务器配置中设置: // Cache-Control: no-cache, no-store, must-revalidate
调试技巧
javascript// 查看当前 Service Worker 状态 navigator.serviceWorker.ready.then(registration => { console.log('Active:', registration.active); console.log('Installing:', registration.installing); console.log('Waiting:', registration.waiting); }); // 强制更新 navigator.serviceWorker.getRegistration().then(reg => { reg.update(); }); // 注销 Service Worker navigator.serviceWorker.getRegistration().then(reg => { reg.unregister(); });
总结
- 自动检测:浏览器自动检测 Service Worker 文件变化
- 安装等待:新版本安装后默认进入等待状态
- 激活接管:旧页面关闭后新版本激活
- 缓存清理:activate 阶段清理旧版本缓存
- 用户提示:提供友好的更新提示机制