这类问题看似集中在一个 API 或参数上,真正决定可用性的却是状态归属、资源上限和失败后的恢复方式。 强制 skipWaiting 能快速发布,却可能让当前页面继续运行旧 JavaScript、后续请求进入新 Service Worker。若两版缓存、API 或消息协议不兼容,一次普通刷新就会把任务切成两半。
浏览器能力由版本、权限、页面可见性和设备策略共同决定。实现需要能力探测与可解释降级,不能把某一台开发机上的 API 存在当成产品契约。
先把实现决策说清楚
实现之前先列出不可违反的不变量,再为性能与体验留出调节参数。参数可以灰度,身份、权限和终态规则不能在运行时随意漂移。
- 构建版本写入页面与 worker,激活前查询是否存在传输或共享中的活跃任务。
- 默认让新 worker waiting,并提供可理解的“任务结束后更新”与手工立即更新。
- 缓存使用版本化名称,activate 只删除已无客户端使用的旧版本。
Service Worker 更新是运行中版本迁移,不是简单替换文件。让激活时机服从活跃任务,并把协议兼容写进发布测试。
最容易漏掉的失败路径
失败路径需要与成功路径共享同一套状态模型。只弹一个错误提示却不释放资源、不传播终态,下一次恢复就会继承脏状态。
- 自动 claim 所有客户端,旧页面发出的消息被新 worker 按新 schema 解析失败。
- 更新提示无限重复,因为页面没有在 reload 后确认已运行目标版本。
- 离线时删除旧缓存后新资源下载失败,应用壳和进行中任务同时不可用。
上线前怎样验证
测试不能以“最后成功了”结束。还要测量经历了多少次副作用、等待多久、是否泄露内容,以及失败后再次执行能否从干净基线开始。
- 在文件传输 1%、50% 与校验期发布新 worker,验证任务策略一致。
- 让多个标签分别运行两版,逐个关闭并确认旧缓存只在最后客户端离开后删除。
- 更新下载中断网再恢复,确认仍可启动旧壳且不会进入半更新状态。
当实现能够安全降级、重复执行不产生额外副作用、故障指标早于投诉出现时,这项能力才算具备长期维护价值。