核心在于让service worker更新与cdn缓存失效对齐:通过构建哈希嵌入sw.js路径(如/sw.abc123.js)并设cdn缓存为no-cache,确保新worker可下载;cachename绑定相同哈希(如app-abc123),activate阶段只清理旧缓存;html及静态资源均哈希化,html设短缓存(max-age=300),带哈希资源设长缓存;辅以registration.update()主动检查和x-app-version响应头校验实现安全回退。

核心在于让 Service Worker 的更新触发点与 CDN 缓存失效边界对齐——版本号哈希值不是用来“防缓存”,而是作为缓存键(cache key)和激活开关的双重标识,避免 CDN 把旧版 sw.js 或资源长期锁住,导致新 Service Worker 根本无法下载、安装。
版本哈希必须参与 Service Worker 脚本 URL
CDN 缓存通常基于请求 URL。如果 sw.js 总是同一个路径(如 /sw.js),即使内容已更新,CDN 可能仍返回旧版本,造成“新逻辑没加载,旧 worker 一直拦着请求”。解决方式是把构建哈希嵌入脚本路径:
- 构建时生成类似 /sw.abc123.js 的文件名,其中 abc123 是本次构建产物的唯一哈希(如基于 public/sw.js + webpack config + manifest 内容计算)
- 注册时明确指定带哈希的路径:
navigator.serviceWorker.register('/sw.abc123.js', { scope: '/' }) - 确保该路径不被 CDN 长期缓存(例如设置 Cache-Control: no-cache, must-revalidate 或 max-age=0),因为它是“入口指针”,必须实时反映最新版本
缓存名称(cacheName)与哈希强绑定
Service Worker 的 install 阶段会打开一个命名缓存(如 app-v1.2.0-xyz789)。这个名称必须包含与 sw.js 一致的哈希值,否则会出现“新 worker 想用新缓存,却去删了旧缓存名,而旧缓存里还存着上一版 HTML”这类错位问题:
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
- 推荐格式:cacheName = `app-${BUILD_HASH}`(例如 app-xyz789)
- 在 activate 阶段,只清理不匹配当前 BUILD_HASH 的缓存:
caches.keys().then(keys => Promise.all(keys.filter(k => !k.startsWith(`app-${BUILD_HASH}`)).map(caches.delete))) - 这样保证每次发布,缓存空间干净隔离,不会因 CDN 滞留旧资源而污染新缓存
静态资源也需哈希化并配置 CDN 短缓存
HTML、JS、CSS 等资源若未带哈希后缀,CDN 可能缓存数小时甚至数天。当用户已安装 PWA,浏览器会先加载 index.html —— 如果 CDN 返回的是旧 HTML,它里面引用的仍是旧 sw.js 地址,整个更新链就断了:
- 所有构建产物启用 contenthash(如 Webpack 的
[contenthash]),输出 main.a1b2c3.js、style.d4e5f6.css - 在 index.html 中通过构建工具注入带哈希的资源链接,确保 HTML 本身成为“版本快照”
- CDN 对 HTML 设置 max-age=300(5 分钟)或使用 ETag;对带哈希的静态资源可设长缓存(如一年),因其文件名已天然唯一
主动检查 + 安全回退机制补位
即便做了上述隔离,仍可能遇到极端情况:用户长时间未打开页面,CDN 滞留旧 HTML,且其内注册的旧 sw.js 已失效。这时需要前端兜底:
- 页面加载后,调用
registration.update()主动拉取最新 sw.js(注意:仅当检测到当前 controller 不是最新版本时才触发) - 监听
controllerchange事件,一旦新 worker 接管,提示用户“新功能已就绪”,并提供手动刷新按钮 - 为关键接口(如获取应用元信息)添加版本字段响应头(如 X-App-Version: xyz789),前端比对失败则强制跳转 reload 页面,避免 UI 与逻辑错配










