Service Worker 需HTTPS/localhost环境、页面重载后才生效,且SW文件须同源并可缓存;通过install预缓存、fetch拦截实现离线访问;fetch响应需clone才能复用;更新依赖skipWaiting与clients.claim。

Service Worker 是浏览器后台运行的脚本,它能拦截网络请求、缓存资源、实现离线访问——但它不是全局可用的,必须满足 HTTPS(或 localhost)环境,且注册后不会自动接管页面。
Service Worker 为什么不能直接用 navigator.serviceWorker.register() 就生效?
注册只是告诉浏览器“有这么个文件”,真正生效需要两个条件同时满足:
- 页面已通过
https://或http://localhost加载(file://协议完全不支持) - Service Worker 脚本注册成功后,必须重新加载页面(或等待当前页面关闭再打开),才能被该页面控制
- Service Worker 文件本身需与页面同源,且响应头不能含
Cache-Control: no-store等禁止缓存的指令(否则浏览器可能反复拉取新版本失败)
如何用 cache.addAll() 和 fetch 事件实现基础离线能力?
核心逻辑是:安装阶段预缓存关键资源;运行时对匹配 URL 的请求优先返回缓存,未命中再走网络。注意 cache.addAll() 只接受相对路径(相对于 SW 文件位置),且会因任一资源失败而整体拒绝 Promise。
self.addEventListener('install', event => {
event.waitUntil(
caches.open('v1').then(cache =>
cache.addAll([
'/',
'/index.html',
'/styles/main.css',
'/scripts/app.js'
])
)
);
});
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request).then(response => {
return response || fetch(event.request);
})
);
});
为什么 fetch() 返回的 Response 不能直接 put() 到 Cache?
因为 fetch() 返回的 Response 流体(ReadableStream)只能读取一次。若想既返回给页面又存入缓存,必须用 response.clone() 分出副本:
立即学习“Java免费学习笔记(深入)”;
- 不 clone 直接
cache.put(req, res)→ 第二次读取时报错TypeError: Failed to execute 'put' on 'Cache': Request body is already used - 正确做法是:
cache.put(req, res.clone()),然后用原res响应页面 - 也别忘了检查
res.ok,避免把 404/500 错误页也缓存进去
离线应用真正的难点不在缓存,而在更新策略和生命周期控制
用户刷新页面时,浏览器可能还在用旧版 Service Worker,即使你已部署新 SW 文件。它只会在下次导航时静默下载并进入 waiting 状态——除非你主动调用 skipWaiting(),或让用户点击“刷新”触发 clients.claim()。这些细节不处理,离线功能看似正常,实则长期无法更新缓存内容。











