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

Service Worker 的更新机制是怎样的?

3月7日 12:06

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 示例

javascript
function 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(); });

总结

  1. 自动检测:浏览器自动检测 Service Worker 文件变化
  2. 安装等待:新版本安装后默认进入等待状态
  3. 激活接管:旧页面关闭后新版本激活
  4. 缓存清理:activate 阶段清理旧版本缓存
  5. 用户提示:提供友好的更新提示机制
标签:Service Worker