返回知识中心
部署

Service Worker 更新与客户端一致性

用可验证的状态理解 Service Worker 更新发现、waiting 与 active、受控客户端、安全重载、回滚证据和发布验证。

BotBrowser Team

文档中心

想直接进入 部署 文档吗?

这篇文章属于博客内容库。若你要步骤化配置、参考说明和持续更新,请直接进入对应 docs 分区。

从发现更新、安装和等待,到激活、controller 变化、协调重载、验证与回滚证据的流程图

BotBrowser 可以创建受控浏览器上下文,并暴露测试所观察到的注册信息和客户端状态。它不会改变浏览器的 Service Worker 更新算法,不会把 waiting worker 强行变为 active,也不会替应用决定何时可以安全重载。协调契约由应用负责;BotBrowser 提供可重复的上下文和观测点。

TL;DR

更新不是一次下载,而是一串状态变化。使用 ServiceWorkerRegistration.update() 发现候选版本,观察 installing、waiting 和 active,确认哪些客户端受控,然后只在新版本已准备好且与这些客户端兼容时协调重载。记录旧、新脚本身份、客户端状态、用户选择和重载后的结果。如果新版本出错,停止继续推广,恢复最后一个已知正常的脚本,再用新证据验证旧版本。本流程不承诺所有浏览器在同一时刻收到更新。

Contents

理解更新状态

浏览器评估候选脚本时,会继续保留现有的 active Service Worker。因此,一个注册信息可以同时暴露三个不同引用:候选正在安装时是 installing,安装结束但尚未接管时是 waiting,当前为受控客户端提供服务的是 active。这些是需要读取的状态,不等同于“服务器上已经有新文件”。

registration.update() 会请求浏览器检查注册信息的脚本 URL 是否有新版本。MDN 参考说明它返回一个 promise:检查完成后以注册信息解析,无法完成更新时拒绝。promise 解析并不证明找到了新版本或已经激活。解析后仍要读取 registration.installing、registration.waiting 和 registration.active,并监听异步出现候选版本的 updatefound。

安装中 worker 的 statechange 是显示更新提示的有用边界。installing 表示候选尚未准备好协调重载。如果已有 active worker 或受控客户端,installed 可以停留在 waiting;首次安装且没有 active worker 时,它会进入 activating 和 activated。installed 本身不等于 active。activating 是过渡状态,不是稳定的发布身份。activated 证明注册信息已有新的 active worker,但页面仍可能需要重载后才受其控制。

可以用小型状态表让每个转换只有一个含义:

观察到的状态安全解释下一步
installing候选版本正在准备保持当前页面,等待 statechange
waiting候选已准备好,但已有客户端可能仍使用旧 active 版本显示带兼容性说明的重载选择
active 且 controller 未变注册信息已 active,但当前页面可能仍由旧版本控制等待 controllerchange 或下一次有意导航
controllerchange当前页面的控制版本发生变化校正页面状态,必要时只重载一次

不要从时间戳、返回 200 的请求或新脚本 URL 推断状态。浏览器可能在不同时间检查,而一个客户端仍由旧 controller 控制时,另一个客户端可能已经前进。

保持受控客户端一致

navigator.serviceWorker.controller 告诉页面当前由哪个 active worker 控制;页面未受控时为 null。把 controller 的脚本 URL 或应用暴露的发布标识,与注册状态一起记录。这样就能区分“候选已安装”和“本页面正在候选版本下运行”。

多个标签页的应用需要对何时前进作出统一决定。一个标签页可以观察到 waiting,并向其他受控客户端广播中性的“更新已准备好”信号。每个标签页都应显示同一个发布标识,并允许用户推迟重载。正在编辑未保存表单的标签页,不应因为另一个标签页已经准备好就被重载。

更新消息应携带事实,而不是隐藏状态转换的命令:候选发布、当前 controller 发布、就绪状态和请求标识。收到消息后,标签页要重新读取自己的注册信息和 controller。这样可以避免在第二次更新到来后仍依赖过时消息。

兼容性是决定规则。若候选版本改变响应格式、导航假设或页面协议,就不应在没有迁移路径时接管仍打开的旧页面。旧、新页面无法共存时,应标记为“需要一起重载”。即使候选版本向后兼容、提示可以更温和,也仍要显式检查 controller。

BotBrowser 可以打开多个受控上下文并捕获每个页面的注册状态,但不能让两个上下文共享 controller,也不能替产品判断页面协议是否兼容。测试必须说明哪些客户端需要一起前进,哪些可以继续使用旧版本。

协调安全重载

先在页面界面中宣布更新已准备好,不要直接导航。显示候选发布标识和重载原因。只有在页面安全时才保留重载选择:不能有会丢失的未保存表单、上传、支付步骤或其他操作。选择“稍后”的用户应能在下一次导航时继续。

然后在重载前立即确认状态。读取 registration.waiting、active worker 和 navigator.serviceWorker.controller,把它们的标识与打开提示的消息进行比较。如果不一致,关闭提示并重新检查,不要基于旧信息重载。

最后协调转换。页面可以监听 controllerchange,停止接收新工作,保存产品允许保存的应用状态,然后只重载一次。用每页标记保护重载,避免事件突发造成循环。已经开始导航的页面收到 controllerchange 时,只需完成导航并记录最终 controller。

下面的最小流程只观察状态,不强行激活:

let reloadIssued = false;
let reloadApproved = false;

function approveReload() {
  reloadApproved = true;
}

navigator.serviceWorker.addEventListener('controllerchange', () => {
  if (reloadIssued || !reloadApproved) return;
  if (document.querySelector('[data-unsaved-work="true"]')) return;
  reloadIssued = true;
  window.location.reload();
});

async function checkForUpdate(registration) {
  await registration.update();
  return {
    installing: registration.installing?.state ?? null,
    waiting: Boolean(registration.waiting),
    active: registration.active?.scriptURL ?? null,
    controller: navigator.serviceWorker.controller?.scriptURL ?? null,
  };
}

激活策略必须符合页面协议和用户当前工作。关键表单应让用户先完成,再在下一次导航后确认 controller;只读页面在兼容性已证明时可以提供立即选择。

让回滚证据可用

回滚是由观测支持的发布决定。保留最后已知正常的脚本身份、候选身份、浏览器版本、上下文标识和失败的完整用户旅程。为更新发现、激活、controller 变化和重载完成记录时间。证据需要状态转换,不需要账号数据或请求正文。

检查点保留证据决定
推广前已知正常脚本身份和通过的旅程结果基线可以恢复
发现候选update() 结果、注册状态、候选身份候选确实被观察到
重载后controller 身份、页面发布、旅程结果客户端一致前进或未前进
回滚后恢复脚本身份和新一次旅程结果回滚有效,而不只是配置存在

旅程失败时,停止继续推广,并将 controller 身份与预期发布进行比较。通过正常发布路径恢复最后已知正常脚本,在新的上下文中重复同一旅程。已经被候选版本控制的页面可能需要协调导航后才能证明恢复版本;要记录这一点,不能过早宣布回滚完成。

“旧文件仍可访问”不是回滚证据。有效证据是受控客户端报告已恢复的 active 版本,并通过曾失败的旅程。保留候选产物和失败记录用于诊断,但面向用户的路径只提供已接受版本。

验证一次发布

在可重复的上下文中覆盖完整序列:

  1. 加载基线,记录注册信息、active 脚本、controller 和代表性旅程结果。
  2. 部署候选脚本,从已经受控的页面调用 registration.update()。
  3. 记录 installing、waiting、active、updatefound 和 statechange,直到候选进入稳定状态。
  4. 打开第二个受控客户端,确认它按兼容性规则继续旧 controller 或前进。
  5. 在一个客户端选择重载,捕获 controllerchange,确认页面最多重载一次。
  6. 重复旅程并与基线比较。结果不一致就暂停推广。
  7. 用同一上下文矩阵运行回滚路径,并保留恢复版本证据。

BotBrowser 能让这些检查在不同受控上下文和浏览器版本上重复执行,但不能认证应用的兼容性契约、替用户选择重载时机,也不能保证统一的更新时间表。这些结论必须由页面和发布过程的产品证据支持。

更广泛的浏览器版本基线可参阅浏览器发布验证;网络中断后的会话恢复可参阅浏览器会话恢复。

不要只记录“更新成功”。对每个上下文记录浏览器版本、注册脚本 URL、installing、waiting、active,以及提示前后的 controller。还要记录用户选择“现在”还是“稍后”,因为延后是有效结果,不是失败。只写一个成功标签,无法区分候选已安装、已经 active,还是已经控制页面。

比较具体的身份,不要只比较显示名称。带有不可变发布标识的脚本 URL,能让页面和测试进行明确比较。如果页面显示的发布值与 controller 值不同,保持当前页面,重新读取状态,不要把不确定的观测变成强制导航。多个标签页也要分别记录,因为它们可能在不同状态、没有 controller,或已经由候选版本控制。

更新检查应有明确上限。只有在上一次 promise settled 后才再次调用 update(),并把检查次数写入记录。重复调用不会加快激活,无限循环还会掩盖服务器或兼容性问题。达到上限后保留观测结果,再作发布决定。

提示文案应与状态相符。只有实际观察到 waiting 或新的 active 身份时才说“新版本已准备好”;update() 进行中应说“正在检查”;promise rejected 时说明检查未完成,并让当前页面继续可用。重载按钮在上传或支付期间应禁用,“稍后”保持可用,操作结束后重新检查身份再导航。

验收记录要让没有执行测试的人也能理解。写出页面旅程、每次 controller 转换和回滚结果,保留已知正常身份与一次新的通过结果,避免原始控制台输出和页面敏感内容。这样才能把一次观测与另一个浏览器版本或页面协议区分开。

支持团队还应记录页面是否发出 controllerchange。后台定时检查只能更新状态指示器和注册快照,不能丢弃用户输入;浏览器在导航或功能事件中自行检查时,也要使用同一个状态模型。

统一术语有助于排查:用“候选”表示 installing 或 waiting 版本,用“active”表示注册信息的 active 引用,用“controller”表示当前控制页面的版本。若候选被拒绝,记录失败状态并为下一候选使用新的身份,避免混淆证据。

验收时逐项回答:是否观察到 update() 完成和候选身份,哪些客户端按规则前进,重载前是否确认身份且没有受保护的未保存操作,是否最多发生一次重载,以及已知正常身份和新的通过旅程是否足以证明回滚有效。这个结论只适用于声明过的上下文矩阵,脚本或页面协议变化后要重做。

实用结论

把 Service Worker 更新看成注册信息、候选版本、active 版本和受控客户端之间的协调。用 update() 发现变化,读取真实状态,明确兼容性,让每个客户端只在确认转换后重载,并把回滚证据绑定到真实 controller 和可重复旅程。BotBrowser 适合复现这些观测,但更新策略仍由应用负责。

Sources

#Service Worker#浏览器更新#发布验证#客户端协调

让 BotBrowser 从研究走向生产

先用这些指南理解模型,再进入跨平台验证、隔离上下文和面向规模化的浏览器部署。