Service Worker 生命周期
约 686 字大约 2 分钟
PWAService Worker
2026-04-08
什么是 Service Worker
Service Worker 是运行在浏览器后台、独立于网页主线程的脚本,充当 Web 应用与网络之间的代理,可以拦截网络请求、管理缓存、推送通知、后台同步。它是 PWA 离线能力的核心。
特点
- 运行在独立线程(Worker),无法访问 DOM
- 只能通过
postMessage与页面通信 - 仅在 HTTPS 下运行(localhost 除外)
- 事件驱动,空闲时会被浏览器终止以节省资源
- 全异步,不支持同步 API(如
XMLHttpRequest、localStorage)
注册
// main.js
if ('serviceWorker' in navigator) {
window.addEventListener('load', async () => {
try {
const reg = await navigator.serviceWorker.register('/sw.js', {
scope: '/'
});
console.log('SW 注册成功,scope:', reg.scope);
} catch (err) {
console.error('SW 注册失败:', err);
}
});
}scope 规则:Service Worker 只能控制 scope 路径及其子路径。sw.js 的默认 scope 是它所在的目录,因此通常把 sw.js 放在网站根目录。
生命周期
Service Worker 的生命周期包括:Parsed → Installing → Installed(Waiting) → Activating → Activated → Redundant。
register()
│
▼
┌─────────┐ install ┌───────────┐ activate ┌──────────┐
│ Parsed │──────────▶│ Installed │────────────▶│ Activated│
└─────────┘ │ (Waiting) │ └─────┬────┘
└───────────┘ │
▼
┌──────────┐
│ Redundant│
└──────────┘1. install 事件
首次注册或检测到 sw.js 字节变化时触发。通常用于 预缓存 静态资源。
const CACHE_NAME = 'app-v1';
const PRECACHE_URLS = ['/', '/index.html', '/app.css', '/app.js'];
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => cache.addAll(PRECACHE_URLS))
);
});event.waitUntil(promise):延长安装阶段,直到 Promise resolve;reject 则安装失败。self.skipWaiting():跳过 waiting 阶段,直接激活新 SW(慎用,可能导致新旧资源混用)。
2. waiting 阶段
当页面仍被旧 SW 控制时,新 SW 会进入 waiting。只有当所有使用旧 SW 的页面关闭后,新 SW 才会激活。
3. activate 事件
激活阶段,常用于 清理旧缓存:
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then((keys) =>
Promise.all(
keys.filter((k) => k !== CACHE_NAME).map((k) => caches.delete(k))
)
).then(() => self.clients.claim())
);
});clients.claim():让新 SW 立即接管 所有已打开的页面,而无需刷新。
4. redundant
SW 被替换或注册失败时进入此状态,随后被销毁。
更新机制
浏览器在以下时机检查更新:
- 页面导航时
- 每次触发 SW 事件时(最多每 24 小时一次)
- 手动调用
registration.update()
检测更新并提示用户刷新:
const reg = await navigator.serviceWorker.register('/sw.js');
reg.addEventListener('updatefound', () => {
const newWorker = reg.installing;
newWorker.addEventListener('statechange', () => {
if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
// 有新版本可用
showUpdateToast(() => {
newWorker.postMessage({ type: 'SKIP_WAITING' });
});
}
});
});
// sw.js
self.addEventListener('message', (e) => {
if (e.data?.type === 'SKIP_WAITING') self.skipWaiting();
});
// 监听新 SW 激活后刷新页面
let refreshing = false;
navigator.serviceWorker.addEventListener('controllerchange', () => {
if (refreshing) return;
refreshing = true;
window.location.reload();
});页面与 SW 通信
// 页面 → SW
navigator.serviceWorker.controller?.postMessage({ type: 'HELLO' });
// SW → 页面(广播所有客户端)
self.clients.matchAll().then((clients) => {
clients.forEach((c) => c.postMessage({ type: 'WORLD' }));
});注销
const reg = await navigator.serviceWorker.getRegistration();
await reg?.unregister();调试
Chrome DevTools → Application → Service Workers:
Update on reload:每次刷新强制更新 SW(开发期必开)Bypass for network:跳过 SW 直接走网络Offline:模拟离线skipWaiting:手动让 waiting 中的 SW 立即激活
