Service Worker注册失败主因是未走HTTPS(localhost除外)、路径错误或sw.js不可访问;install阶段cache.addAll()因原子性易全盘失败;fetch事件必须精准拦截request.destination==='document'才能避免白屏。

HTML离线缓存唯一可行路径是 Service Worker,所有 manifest 方案已彻底废弃;不走 HTTPS(localhost 除外)、路径写错、sw.js 不可访问,注册会静默失败。
Service Worker 注册为什么总没反应
注册失败几乎从不报错,只在控制台显示 Failed to register a ServiceWorker,然后终止。真正卡住的永远是这三点:
- 页面必须运行在
https://或http://localhost下——http://127.0.0.1、http://192.168.x.x、file://全部不支持 -
sw.js必须能被浏览器直接请求:打开https://yoursite.com/sw.js应返回 JS 内容且状态码为 2xx;404、302、500、Nginx 返回Cache-Control: no-store都会导致注册中断 - 注册路径必须是根相对路径:
navigator.serviceWorker.register('/sw.js')✅,写成'sw.js'或'./sw.js'❌(它会按当前页面 URL 解析,极易 404)
验证是否成功:DevTools → Application → Service Workers,看到 “Active” 才算跑起来;若一直是 “Waiting”,说明新版本被旧 SW 卡住,需手动 unregister 或调用 skipWaiting()。
install 阶段 cache.addAll() 为什么一个资源都没缓存
cache.addAll() 是原子操作:列表中任意一项 fetch 失败(404、CORS 拒绝、MIME 不匹配、响应头含 no-store),整个缓存就中止,缓存空间里空空如也。
立即学习“前端免费学习笔记(深入)”;
- 路径必须是绝对根路径:
'/index.html'✅,'index.html'❌,'./style.css'❌,'/static/app.js?v=2.1'❌(查询参数不同 = 不同 key,浪费空间且无法复用) - HTML 文件不能单独缓存——它引用的所有
<script>、<link>、<img>资源都得列进urlsToCache,漏一个,离线时就会net::ERR_FAILED - 动态生成的 HTML(如 SSR 页面)若带
Cache-Control: no-store,cache.addAll()会直接拒绝缓存它;建议改用cache.put()+fetch()+response.clone()手动处理
调试建议:在 install 事件里加 console.log('caching:', urlsToCache),并逐个 fetch(url).then(r => r.ok ? r : Promise.reject(r)) 校验。
fetch 事件里为什么页面离线还是白屏
最常见错误是没专门拦截 request.destination === 'document' 的请求。用户输入地址、点击链接、刷新页面——这些行为触发的请求 destination 都是 'document',不命中缓存,页面必然白屏。
- 错误写法:
event.respondWith(caches.match(event.request))—— 它会把 POST 请求、带credentials: 'include'的登录接口也去匹配缓存,导致表单静默失败、登录态丢失 - 正确做法:先判断
if (event.request.destination === 'document'),再caches.match('/') || caches.match('/index.html');没命中则 fallback 到预存的offline.html字符串响应 - 其他类型必须分流:
=== 'script'/=== 'style'可直接 match;=== 'json'或空字符串(如fetch('/api/user'))应放行网络或走 stale-while-revalidate,别塞进主缓存
SPA(如 Vue Router history 模式)下,所有路由路径(/user、/settings)都得在 install 阶段一并缓存,否则离线跳转仍白屏。
缓存更新后页面还是旧内容
旧缓存不会自动清理,新 SW 安装后若不显式删除旧缓存名,浏览器可能继续用过期的 app.js,而你完全感知不到。
- 缓存名不能写死
'v1',建议带构建哈希:static-v${VERSION}(Webpack 可用DefinePlugin注入) - 必须在
activate事件里调用caches.delete()清理非当前版本的缓存,否则旧缓存永远占位 - 不要依赖
self.skipWaiting()强制激活——它会让新 SW 立即接管已有页面,可能导致 JS 版本错配(HTML 是新版,但app.js还是旧版)
最容易被忽略的是服务器配置:Nginx/Apache 若对 sw.js 或静态资源加了 Cache-Control: no-cache 或 no-store,SW 会拒绝注册或缓存失败——必须确保它们返回可缓存的响应头。



















