移除有 bug 的 Service Worker

有时,一个有 bug 的 Service Worker 会被部署, 其次是存在问题 例如,系统可能会在注册时解析 Service Worker,并成功完成安装。 然而,fetch 事件中有 bug 的代码可能会导致它不响应请求, 生成空白页。还有一种可能是网页标记被主动缓存 而 Service Worker 则仅从 Cache 实例针对后续访问返回过时的标记响应。

Service Worker 有很多方式可能会适得其反, 在生产网站上出现这个问题非常可怕 即便如此,一切也不会丢失。有一些办法可以解决这个问题,让业务重回正轨。

部署无操作 Service Worker

要处理有缺陷的 Service Worker,通常只需要部署一个基本的 no-op 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 后,有 bug 的 Service Worker 可以进行修复,并稍后作为更新进行部署。

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

要采取的其他措施

部署一个空操作 Service Worker 应该足以消除有问题的 Service Worker, 但如有必要,可采取其他措施。

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

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

幸运的是,带有 Service Worker 脚本的请求会发送一个有用的 HTTP 请求标头: Service-Worker。 在网络服务器上,检查此标头并拦截请求,以改为提供无操作 Service Worker。 能否实现这一功能取决于所使用的网络服务器和后端堆栈,因此请参阅相关语言的文档,了解如何实现这一目标。

对于未来的 Service Worker 部署,请继续使用无版本控制的资源名称(例如 sw.js)。 这会在后面的工作中大大降低复杂性。

设置 Clear-Site-Data 标头

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

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

因为对此标头的支持并非全部,所以不能仅依赖它来解决问题。 因此,最好将 Clear-Site-Data 视为部署无操作 Service Worker 以外的一项措施。

损害并非永久性

如果用户体验因漏洞的 Service Worker 而中断,这可能会令人望而生畏(特别是对于大型知名网站而言),但这种损害是暂时性的,而且是可逆转的!

如果有必要部署空操作 Service Worker 来解决问题, 在事后花些时间找出问题所在。 将来,请确保 Service Worker 只处理它预期的请求。 在预演阶段频繁进行测试,并且仅在有信心时部署更新。