service worker 的 install 和 activate 阶段实现缓存版本无感升级:install 阶段静默预缓存新资源且不干扰旧服务,activate 阶段有序清理旧缓存并正式接管请求,通过版本隔离、状态解耦与时机控制确保平滑过渡。

Service Worker 的 install 和 activate 阶段,是实现缓存版本平滑无感升级的核心控制点。它不依赖强制刷新或用户操作,而是通过状态隔离与渐进接管,让新旧缓存版本自然过渡,避免页面异常、资源错乱或数据丢失。
install 阶段:新缓存“静默准备”,不干扰当前运行
当注册新版本 Service Worker 脚本(如 sw.js?v=2)时,浏览器会下载并触发其 install 事件。此时:
- 新 SW 独立于当前正在运行的旧 SW,不会立即接管任何页面请求
- 通常在
install中调用caches.open('v2').then(cache => cache.addAll([...])),预缓存新版资源,但这些缓存对当前页面完全不可见 - 使用
event.waitUntil()确保缓存写入完成,否则安装失败,新 SW 不会进入下一阶段 - 即使安装成功,新 SW 仍处于 waiting 状态,等待旧 SW 释放控制权
activate 阶段:旧缓存“有序退场”,新缓存“正式上岗”
只有当所有受控页面关闭或刷新后,旧 SW 才会被解除控制,新 SW 进入 activate 阶段。这时才真正执行升级逻辑:
- 在
activate中遍历caches.keys(),识别并删除过期缓存(如'v1'),避免磁盘堆积和误命中 - 可调用
self.clients.claim()让新 SW 立即接管当前页面(适用于首次加载即需新缓存的场景) - 此时
fetch事件开始由新 SW 拦截,所有请求按新版缓存策略(如cache-first)执行,用户感知不到切换过程 - 若页面未关闭,旧 SW 继续服务,新旧缓存并存但互不干扰——这是“无感”的关键前提
多标签页场景下的版本一致性保障
用户可能同时打开多个同源 Tab,导致旧版与新版 SW 并行存在。为避免缓存混乱:
- 每个缓存命名应包含明确版本标识(如
'my-app-v2'),禁止复用相同 cacheName - 激活时清理逻辑必须严格匹配命名规则,只删非当前版本的 cache,不碰正在使用的
- 可通过
skipWaiting()+clients.claim()主动跳过 waiting 状态,适用于发布紧急修复且允许当前页立即更新的场景 - 不建议在 install 中直接清理旧缓存——那会导致新 SW 安装失败时,旧缓存已被误删而无法回退
缓存升级的“无感”本质是状态解耦与时机可控
真正让用户无感知的,不是技术有多隐蔽,而是每个环节都遵循确定性规则:
- install 只准备,不替换;activate 只清理,不预热;fetch 只响应,不决策——职责清晰
- 缓存内容与 SW 版本强绑定,不同版本间物理隔离,不存在“混用资源”风险
- 页面是否受新 SW 控制,取决于其加载时机(是否由新 SW 提供),而非 SW 自身状态,因此刷新即生效,无需额外干预











