Service Worker注册失败主因是HTTPS缺失、路径错误或响应头禁止缓存;离线逻辑不触发必先确认注册是否成功,再排查install中断或fetch未按destination分流。

Service Worker 注册失败,页面根本没走离线逻辑
不是代码写得不对,而是注册压根没触发。打开 DevTools → Application → Service Workers 面板,如果显示 “No service worker registered” 或面板空白,说明 navigator.serviceWorker 没生效。
常见原因有这几个:
-
navigator.serviceWorker是undefined:当前页面没走 HTTPS(localhost除外),浏览器直接禁用 API -
sw.js返回 404、302 或 500:路径写错(比如注册时写/js/sw.js,但文件实际在/sw.js),或服务器重定向了 -
sw.js响应头含Cache-Control: no-store或no-cache:Nginx/Apache 默认可能加了这类头,浏览器拒绝执行 - 已有激活中的 SW 卡在
waiting状态:新版本不会自动接管,需手动点 DevTools 里的 “Skip Waiting”,或在新 SW 中调用self.skipWaiting()
install 阶段缓存中断,HTML 依赖链一断全崩
cache.addAll() 是原子操作——列表里任意一个资源 fetch 失败,整个缓存就中止,SW 永远卡在 installing 状态,后续 fetch 事件也不会触发。
典型失败场景:
立即学习“前端免费学习笔记(深入)”;
- 写了
['/index.html'],但服务端返回的是动态 HTML,且带Cache-Control: no-store→ 缓存被直接拒绝 - 路径写成相对形式,比如
sw.js在/js/sw.js下注册,却写./style.css→ 解析为/js/./style.css,404 - 清单里写
/images/logo.png,实际文件是/images/Logo.PNG(Linux 服务器区分大小写)→ 404 - 缓存了
/index.html,但没缓存它引用的/app.js或/main.css→ 断网时 HTML 加载成功,但内嵌脚本 404,页面白屏或功能残缺
建议:所有路径必须用绝对根路径(以 / 开头);install 前加 console.log('caching:', urlsToCache);对关键 URL 手动 fetch(url).then(r => r.ok ? r : Promise.reject(r)) 校验。
fetch 事件里无差别 match,POST 和登录态全乱套
只写 caches.match(event.request).then(r => r || fetch(event.request)) 是危险操作。它会让 Service Worker 把 POST 请求、带 credentials 的请求也去缓存查一遍,结果就是表单静默失败、登录态丢失、用户以为提交了但什么都没发生。
必须按 event.request.destination 分流处理:
-
event.request.destination === 'document':这是地址栏输入或刷新触发的主文档请求,必须命中缓存,否则白屏;建议 fallback 到预存的'/offline.html'字符串响应 -
=== 'script'或=== 'style':可直接caches.match()返回,静态资源缓存优先没问题 -
=== 'font'或=== 'image':建议加try/catch,失败就跳过缓存,避免单个图标缺失阻断整页渲染 -
=== 'json'或空字符串(如fetch('/api/user')):这类 API 请求应放行到网络,或走stale-while-revalidate,别塞进离线主缓存
缓存命名不带版本,旧缓存永远占位
缓存名写死成 'v1' 看似简单,但更新资源后,新 SW 安装时旧缓存不会自动删。浏览器可能继续用过期的 app.js,而你根本不知道。
正确做法:
- 缓存名显式带哈希或时间戳,例如
static-v${VERSION},其中VERSION来自构建时注入(Webpack 的DefinePlugin) -
activate阶段必须调用caches.delete()清理非当前版本的缓存,否则旧缓存永远占位 - 不要依赖
self.skipWaiting()强制激活——它会让新 SW 立即接管已有页面,可能造成 JS 版本错配
最易忽略的一点:HTML 文件本身若带查询参数(如 /index.html?v=1.2),不同参数会被当不同键,快速撑爆 CacheStorage 限额。路径必须干净,版本控制交给缓存名本身。



















