开发环境禁用service worker是官方设计,因热更新与缓存冲突;生产构建后通过文件内容哈希实现精准增量更新,workbox自动生成带哈希的sw并注入html,配合skipwaiting与缓存清理确保即时生效。
vue 开发环境下看不到 service worker,不是配置出错,而是官方刻意为之。热更新和 sw 缓存机制天然冲突——你改一行代码,webpack 生成新文件,但若 sw 拦截请求并返回旧缓存,页面就永远不刷新。所以 开发阶段禁用 sw 是保调试体验的必要设计,真正起效只在生产构建(npm run build)之后。
为什么必须用文件哈希做比对
单纯比版本号(如 "v1.2.0")不可靠:人工维护易出错、发布漏打标签、灰度时多版本共存都会导致误判。而内容哈希(contenthash)由文件内容生成,哪怕只改一个空格,JS 文件名就变(如 app.a1b2c3d4.js → app.e5f6g7h8.js)。Service Worker 拦截请求时,能通过 URL 变化精准感知资源变更,这是实现“增量热更新”的底层依据。
Workbox 如何自动生成并注入 SW
Vue CLI 的 PWA 插件默认启用 GenerateSW 模式,它在 build 阶段自动完成三件事:
- 扫描所有输出的 JS/CSS/HTML 文件,提取带哈希的完整路径(如
/js/app.e5f6g7h8.js) - 根据
runtimeCaching规则生成service-worker.js,内含预缓存清单(precaching manifest) - 修改打包后的
index.html,注入navigator.serviceWorker.register('/service-worker.js')
这个 SW 文件本身也带哈希(如 service-worker.9a8b7c6d.js),确保浏览器不会因缓存旧 SW 而跳过更新检测。
热更新提示与强制激活流程
用户访问时,新 SW 已静默安装完成,但仍在 waiting 状态——直到所有旧页面关闭才激活。要实现“当前页立即更新”,需主动监听并触发:
- 在主应用中监听
controllerchange事件,确认新 SW 已接管 - 调用
registration.waiting.postMessage({type: 'SKIP_WAITING'}) - SW 内监听
message事件,执行self.skipWaiting() - 最后调用
window.location.reload(),让新缓存资源生效
整个过程不刷新服务端、不依赖后端接口,纯前端完成全站增量更新。
关键细节:缓存命名与清理策略
Workbox 默认使用带时间戳或哈希的缓存名(如 workbox-precache-v2-abc123)。每次构建生成新缓存名,旧缓存不会自动删除。必须在 SW 的 activate 阶段显式清理:
- 遍历
caches.keys(),筛选出非当前缓存名的旧缓存 - 对每个旧缓存调用
caches.delete() - 末尾加
self.clients.claim(),确保新 SW 立即控制所有客户端
这一步缺失会导致磁盘缓存持续膨胀,且旧资源可能被意外命中。










