HTML不该进CacheStorage,因其是动态入口,缓存后会导致版本滞留、回滚失控;应采用network-first策略,配合meta指纹校验与SW协同更新,再通过IndexedDB版本号保障数据兼容性。

为什么 HTML 文件本身不该进 CacheStorage
HTML 是页面的入口和状态中枢,一旦被缓存进 CacheStorage,就和 JS/CSS/图片这类静态资源绑死在同一个版本里。用户刷新后看到的永远是旧 HTML,哪怕你已在服务器更新了它——因为 SW 拦截了请求并直接返回缓存副本。这不是“缓存没更新”,而是设计上把动态入口当成了静态资产。
更麻烦的是回滚场景:线上 HTML 有严重 bug,你紧急发版修复,但用户本地 SW 仍 serve 着带 bug 的 HTML,除非手动清缓存或等下次 SW 更新(还依赖用户关闭所有标签页)。这不是延迟,是不可控的停滞。
真正可行的做法是:HTML 走 network-first 或 stale-while-revalidate,其他资源(JS/CSS/字体/图片)才用 cache-first。这样 HTML 总是最新的,而静态资源享受离线命中与快速加载。
如何让 HTML 加载时自动校验 hash 并拒绝脏缓存
单纯靠 SW 拦截 HTML 请求不足够,得在 HTML 响应体里嵌入可验证的指纹,比如在 <head> 中注入:
立即学习“前端免费学习笔记(深入)”;
<meta name="sw-version" content="v2.4.1-9a8b7c">
然后在 SW 的 fetch 事件中检查这个值是否匹配当前预期版本:
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
- 提取响应 HTML 的
<meta name="sw-version">内容,用正则或 DOMParser 解析 - 若解析失败,或内容不匹配预设
CURRENT_HTML_VERSION,则拒绝缓存该响应(return fetch(req)不存入 cache) - 只对校验通过的 HTML 才调用
cache.put(),并确保 key 中包含该 hash(如v2.4.1-9a8b7c/index.html)
这样既保留 HTML 的网络优先性,又避免因 CDN 缓存、代理污染或构建产物未同步导致的“HTML 版本错乱”问题。
热更新 HTML 时如何触发已打开页面的软重载
HTML 变了,但用户正在填写表单或滚动长页,不能粗暴 window.location.reload(true)。正确做法是监听 SW 的 controllerchange 事件,并配合轻量级提示:
- 在页面加载时注册:
navigator.serviceWorker.addEventListener('controllerchange', () => { /* 弹出小 toast 提示“新版本已就绪,刷新可获最新功能” */ }) - SW 端在
activate阶段调用self.skipWaiting(),确保新 SW 尽快接管 - 不要在
controllerchange后立刻 reload:用户可能刚点提交按钮,要等beforeunload或主动点击确认后再执行
注意:这个事件只在新 SW 成功激活后触发一次,且仅对当前 tab 生效;后台 tab 需靠 visibilitychange 补充检测。
IndexedDB 里存 HTML 版本号能解决什么问题
光靠 SW 缓存 HTML 不足以支撑复杂离线流程,比如表单草稿、待同步记录、用户偏好等,这些必须持久化到 IndexedDB。此时 HTML 版本号不是用来控制缓存,而是作为数据兼容性锚点:
- 每次 HTML 升级(如新增字段、改 schema),在 DB 中存一条
{ version: 'v2.4.1', migratedAt: Date.now() } - 页面启动时读取该版本,若低于当前 HTML 所需最低版本,就清空旧表结构并重建(避免
objectStore.createIndex()报错) - 表单提交前校验:若 DB 版本过低,阻止提交并提示“请刷新页面以启用新版功能”
这层校验常被跳过,结果就是用户填完表单却因 schema 不匹配而静默失败——表面看是网络问题,实则是 HTML 与离线存储的隐式耦合断裂了。


















