cache.addAll()是原子操作,任一资源失败(404、跨域、MIME错配等)即导致install中断,fetch事件不触发;缓存清单须用绝对路径、不含查询参数,覆盖HTML及所有加载链资源,并配合activate阶段清理旧缓存和document请求fallback机制。

cache.addAll() 是离线化改造中最常用也最容易翻车的缓存入口,它不接受任何失败——只要清单里有一个 404、一个跨域响应、一个 MIME 类型错配(比如字体被当成 text/plain),整个缓存就中断,install 阶段卡住,后续 fetch 事件根本不会触发。
真正决定离线是否可用的,不是你写了多少行缓存代码,而是缓存清单是否覆盖页面加载链上每一个实际发出的请求。漏掉一个 /fonts/inter.woff2,断网时字体 fallback 就会失败;漏掉一个带哈希的 /js/app.a1b2c3.js,JS 加载就 404,页面白屏。
缓存清单必须用绝对路径,且不含查询参数
相对路径、点号路径、带版本号查询参数的 URL 全部无效:
-
/index.html✅ -
index.html❌(解析为当前路径下,如/blog/index.html) -
./style.css❌(同上) -
/static/app.js?v=1.2❌(v=1.2会让缓存键和实际 fetch 请求不匹配)
构建时若生成带哈希的资源名(如 /js/main.f8a9b2.js),清单里就必须写这个完整路径——不能靠运行时拼接,也不能指望 SW 自动 strip 查询参数。
清单项必须在 install 前可访问且返回 2xx
cache.addAll() 是原子操作,任一请求失败,整个缓存中止。常见踩坑点:
立即学习“前端免费学习笔记(深入)”;
- 服务端对
/返回动态 HTML,且响应头含Cache-Control: no-store→ 缓存直接拒绝 - 路径大小写错误(Linux 服务器区分大小写)→
/Images/logo.pngvs/images/logo.png - sw.js 在
/sw.js,但清单写css/style.css→ 解析为/sw.js/css/style.css,404 - 第三方字体 CDN 返回 opaque 响应(如 unpkg.com 不支持 CORS)→
cache.addAll()拒绝写入
建议做法:在 install 事件前加 console.log('pre-caching:', urlsToCache),并手动 fetch() 校验关键 URL 是否可通、状态码是否为 2xx。
HTML 文件本身必须进缓存,且需 fallback 响应兜底
只缓存 JS/CSS 而不缓存 HTML,断网后首次访问仍会失败——因为浏览器发起的首个 document 请求根本没命中缓存,cache.match() 返回 null,又没 fallback,结果就是空白页。
-
event.request.destination === 'document'的请求必须有确定响应:优先从缓存取,未命中则返回预置的/offline.html字符串或构造的Response - 不要写
caches.match(event.request).then(r => r || fetch(event.request))—— 断网时fetch()抛错,Promise reject,页面白屏 -
/offline.html必须也在缓存清单里,否则 fallback 也会失败
缓存名必须带版本标识,activate 阶段必须清理旧缓存
缓存名写死成 'v1' 看似省事,实则埋雷:新版本 SW 安装后,旧缓存不会自动删,浏览器可能继续用过期的 app.js,而你完全不知情。
- 缓存名建议用构建时注入的哈希或时间戳:
static-v${BUILD_HASH},确保每次部署唯一 -
activate事件中必须调用caches.keys().then(keys => Promise.all(keys.map(key => key !== CACHE_NAME && caches.delete(key))))清理非当前版本缓存 - 别依赖
self.skipWaiting()强制激活——它可能导致页面 JS 和 HTML 版本错配;更稳妥的是监听controllerchange后手动刷新
最常被忽略的其实是调试阶段的协议陷阱:双击打开本地 HTML 文件走的是 file:// 协议,navigator.serviceWorker 直接是 undefined,注册静默失败,连控制台都不报错。必须用 http://localhost 或真实 HTTPS 域名才能验证逻辑是否真生效。



















