Service Worker 缓存名必须带版本号,如pages-v2.1.0,否则无法触发差量更新;HTML结构变更需同步更新缓存名、HTML内容和IndexedDB schema,三者缺一不可。

Service Worker 缓存名必须带版本号,否则无法触发差量更新
HTML 结构本身不参与版本管理——真正起作用的是 cacheName。浏览器不会因为 HTML 文件里写了 v2.1.0 就自动切换缓存,它只认 Service Worker 脚本里 caches.open('pages-v2.1.0') 这个字符串。一旦你把缓存名写成固定的 'pages',后续所有更新都会往同一个桶里塞,旧资源永远残留,新结构读到旧数据就成常态。
常见错误现象:index.html 已改版加了新 class,但离线打开仍渲染旧 DOM;用户刷新后页面空白,控制台报 TypeError: Failed to execute 'put' on 'Cache': Request failed(因旧 SW 正在用同名缓存,新 SW 写冲突)。
- 每次发布必须变更
cacheName,推荐格式:`pages-${VERSION}`或`assets-${BUILD_HASH}` - VERSION 可来自构建脚本注入(如 Vite 的
import.meta.env.VITE_APP_VERSION),避免手写出错 - 不要用时间戳(如
new Date().toISOString())——开发环境热更新会频繁生成新缓存,快速占满配额
HTML 文件不能靠 Cache-Control 头刷新 SW 缓存
Cache-Control: no-cache 或 max-age=0 只影响浏览器 HTTP 缓存层,对 Service Worker 里 caches.put() 存进去的响应体完全无效。你改了 HTML 源码、发了新包,但用户打开还是旧版,大概率是 SW 没重新 fetch 这个文件。
使用场景:HTML 是应用外壳(App Shell),需保证每次启动都加载最新结构,而非复用旧缓存。
立即学习“前端免费学习笔记(深入)”;
- 在 SW 的
install事件中,显式把 HTML 路径加入cache.addAll([...])列表(例如['/index.html', '/offline.html']) - 若 HTML 内容动态生成(如 SSR),则不能预缓存,得改用
fetch+cache.put()在 runtime 拦截并缓存,且需设置合理失效逻辑 - 务必确保 HTML 请求走的是 HTTPS —— HTTP 下 SW 注册失败,缓存根本不会生效
activate 阶段清理旧缓存时,别删正在用的 cache
SW 的 activate 事件是清理旧缓存的唯一安全时机。但直接遍历 caches.keys() 并全删,会导致当前页面还在用的缓存被踢掉,触发 Request failed 错误,离线访问直接中断。
性能影响:清理动作本身很快,但若误删活跃缓存,用户会看到白屏或资源 404,比不清理更糟。
- 清理前先明确当前有效缓存名列表,比如
const CURRENT_CACHES = ['pages-v2.1.0', 'assets-v2.1.0'] - 用
keys.map(key => !CURRENT_CACHES.includes(key) && caches.delete(key))过滤后再批量删除 - 必须搭配
self.clients.claim(),否则新 SW 不接管页面,旧缓存继续被引用,清理无效 - 切勿在
install阶段调caches.delete()—— 此时旧 SW 还在 service 中,删了它正用的缓存等于自断后路
HTML 结构变更时,IndexedDB 数据模型也得同步升级
离线场景下,HTML 结构和本地数据常耦合。比如新版 HTML 增加了 data-user-id 属性,但 IndexedDB 里还存着旧格式的用户记录,JS 渲染时读不到字段,视图就错乱。这不是缓存问题,而是数据契约断裂。
容易踩的坑:只更新 SW 和 HTML,忘了跑数据库 migration。
- 在 SW 的
activate事件里,用indexedDB.open(DB_NAME, NEW_VERSION)触发 upgradeNeeded - upgradeNeeded 回调中用
db.createObjectStore()或store.createIndex()更新 schema,并用游标批量迁移旧数据 - Migration 完成前,不要让页面加载新版 HTML —— 可在 activate 后发消息给页面,等 DB 就绪再
location.reload() - 别依赖 localStorage 存结构版本号:它同步阻塞主线程,大体积数据易卡死 UI



















