manifest属性已完全失效,Chrome≥93、Firefox≥95、Safari≥16.4彻底移除支持,浏览器静默忽略该属性,不解析、不报错、不缓存;唯一有效离线方案是Service Worker,需HTTPS下注册并精确控制缓存逻辑。

manifest 属性已完全失效,不是配置问题,是浏览器主动移除了整套机制。Chrome 93、Firefox 95、Safari 16.4 起均不再解析该属性,页面不会报错,但缓存行为静默消失——你写的 manifest="app.manifest" 就像没写一样。
为什么 manifest 属性现在一点用都没有
这不是兼容性问题,而是标准层面的彻底删除:
- Chrome 自 93 版(2021 年中)起标记为 deprecated,95 版(2021 年底)起完全移除逻辑;2026 年所有稳定版浏览器均已无 manifest 解析能力
-
window.applicationCache在现代浏览器中返回undefined,DevTools 的 Application 面板里连 “Manifest” 标签都消失了 - 即使服务器正确返回
Content-Type: text/cache-manifest,浏览器连请求都不会发出去——解析器代码已被删干净 - 旧项目若还依赖它,在公网环境必然离线失败;内网 Electron 或定制 Chromium 可能残留支持,但需同时满足 HTTPS/首行严格
CACHE MANIFEST/服务端 MIME 配置三重条件,且更新仍不可靠
navigator.serviceWorker.register() 是唯一可行注册入口
Service Worker 不是“另一个选项”,而是当前 Web 平台唯一能接管网络请求、实现可控离线逻辑的机制:
- 必须在 HTTPS 或
localhost下调用,否则navigator.serviceWorker.register('sw.js')直接静默失败(不抛错,也不注册) -
sw.js必须放在根目录或显式指定scope,不能带查询参数(sw.js?v=1会被视为新脚本,导致重复注册或版本混乱) - 首次访问只注册,不激活;第二次加载才触发
install;如需立即生效,必须在waiting状态下调用skipWaiting(),并在activate中调用clients.claim() - 注册时机很重要:放在
<body>底部或DOMContentLoaded后可保首屏资源被拦截;太晚注册(如 ReactuseEffect里)会导致部分资源已发出请求,无法被 SW 拦截
缓存策略不能照搬 manifest 的三段式写法
Manifest 的 CACHE:/NETWORK:/FALLBACK: 是声明式、粗粒度的;Service Worker 要求你显式控制每个请求的流向:
立即学习“前端免费学习笔记(深入)”;
- 静态资源(JS/CSS/图片)适合在
install事件中用caches.open('v1').addAll([...])预缓存,但注意:单个资源失败会导致整个addAll()回滚——建议改用循环 +cache.put()并捕获异常 - HTML 文件不该全量预缓存,推荐 fetch 优先 + 失败 fallback:在
fetch事件中fetch(request).catch(() => caches.match('/offline.html')) - API 请求一般不进 Cache Storage,应依赖 HTTP 缓存头(
Cache-Control)或按需手动cache.put(),避免缓存过期数据 -
FALLBACK映射要转成运行时逻辑:比如所有/api/请求失败时返回{error: "offline"}JSON 响应,而不是简单跳转页面
调试 Service Worker 最容易忽略的三个事实
它不像普通 JS 那样刷新就能重来:
- DevTools 的 Console 默认不显示 SW 的
console.log,必须切换到 Application → Service Workers → “Open in Sources” 或点击 “Inspect” 才能看到上下文输出 - 改了
sw.js内容但页面还在用旧版?检查是否触发了activate:旧版未被skipWaiting()接管前,新脚本只是 waiting 状态 - 缓存清理不干净是常态:每次更新版本名(如
'my-site-v2'),应在activate事件中手动caches.delete('my-site-v1'),否则旧缓存会一直占着空间且可能干扰新逻辑
真正难的不是写几行 self.addEventListener('fetch', ...),而是把缓存生命周期、资源分类、错误兜底和调试闭环串成一条可验证的链路。别指望“配个 manifest 就离线”,现在的离线能力是写出来的,不是声明出来的。



















