移除有 bug 的 Service Worker

有时,部署有缺陷的 Service Worker 之后会出现问题。例如,Service Worker 可能会在注册时解析并成功完成安装。但是,fetch 事件中有 bug 的代码可能会导致其无法响应请求,从而产生空白页。另一种可能性是,网页标记被主动缓存,而 Service Worker 针对后续访问仅从 Cache 实例返回过时的标记响应。

Service Worker 有多种方式可能会适得其反,这在生产网站上是一个可怕的问题。即便如此,你也不会迷失方向。有一些方法可以解决问题,回到正轨。

部署无操作 Service Worker

处理有缺陷的 Service Worker 通常只需部署一个基本的无操作 Service Worker,该 Service Worker 无需 fetch 事件处理脚本即可立即安装和激活:

// sw.js

self.addEventListener('install', () => {
  // Skip over the "waiting" lifecycle state, to ensure that our
  // new service worker is activated immediately, even if there's
  // another tab open controlled by our older service worker code.
  self.skipWaiting();
});

self.addEventListener('activate', () => {
  // Optional: Get a list of all the current open windows/tabs under
  // our service worker's control, and force them to reload.
  // This can "unbreak" any open windows/tabs as soon as the new
  // service worker activates, rather than users having to manually reload.
  self.clients.matchAll({
    type: 'window'
  }).then(windowClients => {
    windowClients.forEach((windowClient) => {
      windowClient.navigate(windowClient.url);
    });
  });
});

此 Service Worker 将通过在 install 事件中调用 self.skipWaiting() 来立即安装并激活。或者,可以在 activate 事件中部署其他代码,以强制重新加载具有 Service Worker 控制的 WindowClient 的任何其他打开的标签页。

空操作 Service Worker 不包含任何 fetch 事件处理脚本,这一点非常重要。当 Service Worker 不处理请求时,这些请求会传递到浏览器,就好像不存在 Service Worker 一样。部署无运维 Service Worker 后,有缺陷的 Service Worker 可以进行修复并在稍后以更新的形式进行部署。

这种方法之所以有效,原因之一是浏览器采取了强有力的保护措施来防止将 Service Worker 置于 HTTP 缓存中,而且它们会对 Service Worker 的内容进行逐字节检查以进行更新。这些默认值可让您为有缺陷的 Service Worker 部署空操作替换,以快速解决问题。

要采取的其他措施

部署无运维 Service Worker 应该足以消除存在 bug 的 Service Worker,但如有必要,也可以采取其他措施。

如果您不知道旧 Service Worker 的网址,该怎么办?

有时,之前安装的 Service Worker 的网址未知。这可能是因为该文件带有版本编号(例如,文件名中包含哈希值)。 在这种情况下,部署一个与每个可能注册的旧 Service Worker 的网址匹配的无操作 Service Worker 并非易事。这违背了最佳做法,因为开发者可能不记得所部署的每个 Service Worker 版本的每个哈希值。

幸运的是,系统会通过 Service-Worker 服务工作器脚本请求发送一个实用的 HTTP 请求标头。在 Web 服务器上,检查此标头并拦截请求,以改为传送空操作 Service Worker。能否实现这一成就取决于所使用的 Web 服务器和后端堆栈,因此请参阅相关语言的文档,了解具体操作方法。

对于将来的 Service Worker 部署,坚持使用不带版本号的资产名称(例如 sw.js)。这将大幅简化后续工作。

设置 Clear-Site-Data 标头

如果设置了值为 'storage'Clear-Site-Data 响应标头,某些浏览器将取消注册某个来源的所有 Service Worker。不过,在使用这种方法时,需要注意以下几点:

  • 请注意,此操作会清除关联源的所有存储空间。这包括 localStorage、IndexedDB、sessionStorage 和其他存储空间(但不包括源的 HTTP 缓存)。
  • 并非所有浏览器都支持此标头。

因为我们不完全支持此标头,因此不能只依靠它来解决问题。 因此,最好将 Clear-Site-Data 视为部署无运维 Service Worker 之外的一项措施。

损坏不是永久性的

如果用户体验被有缺陷的 Service Worker 打断,情况就很可怕,尤其是对于大型知名网站,但这种损害是暂时的,并且是可逆转的!

如果需要部署无操作 Service Worker 来解决问题,请花些时间在事后找出问题所在。今后,请确保 Service Worker 只处理它预期的请求。在预演阶段经常进行测试,只有在信心十足时才部署更新。