Application Cache 已被现代浏览器彻底移除,manifest 属性无效;Service Worker 必须预缓存 HTML 并正确处理 fetch 事件,配合 /version.json 实现可靠离线与版本更新。

manifest 文件已彻底失效,别再写 manifest="app.appcache"
现代浏览器(Chrome 95+、Firefox 85+、Safari 16.4+)已完全移除对 Application Cache 的支持。你加了 manifest="app.appcache",页面空白或控制台报 Manifest: line 1: Syntax error 或直接无反应,不是路径错、不是 MIME 类型没配——是浏览器压根不解析它。DevTools 的 “Application → Manifest” 面板只是 UI 残留,点进去看到的列表可能来自缓存快照,甚至其他域名。删掉所有 manifest 属性和对应 .appcache 文件,否则 Chrome 仍会尝试加载并抛出静默警告。
HTML 文件本身必须被预缓存,否则离线时根本打不开
Service Worker 的 cache.addAll() 是原子操作:只要列表里任一 URL 失败(404、跨域、响应头含 Cache-Control: no-store),整个缓存中止,Worker 卡在 installing 状态,后续 fetch 事件不会触发。而 HTML 是入口,若没被缓存,离线时连首屏都渲染不了。
- 清单里必须显式包含 HTML 路径,如
['/', '/index.html'];推荐用绝对路径,避免相对路径解析错误(比如sw.js在/js/sw.js,却写./index.html→ 解析为/js/./index.html) - 服务端对
/返回的不能是 SSR 动态页且带Cache-Control: no-store,否则缓存失败;建议后端对 HTML 响应头设为Cache-Control: public, max-age=0,让 SW 控制缓存逻辑 - 构建时生成唯一哈希(如
window.__APP_VERSION = "a1b2c3d"),注入 HTML 全局变量,供后续版本比对
版本更新检测不能靠 reload 或时间戳,得靠服务端 /version.json
用户不手动刷新,浏览器就一直用旧缓存的 HTML 和 JS。靠 document.lastModified 或 new Date() 判断毫无意义——它们和部署版本无关。真正有效的检测必须绕过所有缓存层。
-
/version.json必须返回纯文本 JSON,且响应头强制禁用缓存:Cache-Control: no-cache, must-revalidate;Nginx 配置示例:add_header Cache-Control "no-cache, must-revalidate"; - 前端 fetch 该接口,对比本地
window.__APP_VERSION;不一致时,动态创建弹窗 DOM(不能写死在模板里,否则会被缓存) - 弹窗插入前检查
document.body.contains(document.getElementById('update-modal')),防止重复;样式用position: absolute; inset: 0;覆盖全屏,避免position: fixed滚动错位
fetch 事件里只做 caches.match() || fetch() 就是假离线
这种写法会让 HTML 显示出来,但 JS/CSS/图片/接口全崩。因为:
立即学习“前端免费学习笔记(深入)”;
-
caches.match()只查 Cache API 缓存,不查 HTTP 缓存;没预缓存的资源(如 API 接口)必然 miss,fallback 到fetch(),断网时直接net::ERR_FAILED - 必须按
event.request.destination分类处理:'document'请求优先返回缓存兜底;'script'和'style'必须确保已在 install 阶段预缓存;'json'类请求不应缓存旧数据,可返回自定义错误页或空响应 - 不要在 fetch 事件里调
event.respondWith(caches.match(event.request))后直接 return;未命中时需判断 destination 再决定是否走网络或降级
真正的离线能力不在“能打开”,而在“打开后所有依赖资源都能加载”。SW 注册失败、install 阶段卡住、HTML 路径漏写、version.json 被 CDN 缓存——任何一个环节断掉,用户就回到白屏或半功能状态。



















