如何删除有问题的服务工作者,或实施“终止开关”?

IT技术 javascript service-worker
2021-01-14 20:52:11

我正在我的计算机中使用 Service Worker API,这样我就可以了解如何在现实世界的应用程序中受益。

我遇到了一个奇怪的情况,我注册了一个 service worker,它拦截了fetch事件,这样它就可以在向源发送请求之前检查其缓存中的请求内容。问题是这段代码有一个错误,阻止了函数发出请求,所以我的页面是空白的;没发生什么事。由于 service worker 已经注册,我第二次加载页面时它会拦截第一个请求(加载 HTML 的请求)。因为我有这个错误,那个 fetch 事件失败了,它从不请求 HTML,我看到它是一个空白页面。

在这种情况下,我知道删除不良服务工作者脚本的唯一方法是通过chrome://serviceworker-internals/控制台。如果此错误出现在实时网站上,解决它的最佳方法是什么?

谢谢!

6个回答

我想扩展这里的其他一些答案,并从“在将 Service Worker 部署到生产环境时可以使用哪些策略来确保我可以进行任何需要的更改”的角度来解决这个问题?这些更改可能包括修复您在生产中发现的任何小错误,或者可能(但希望不会)包括由于无法克服的错误(即所谓的“终止开关”)而使 Service Worker 失效。

出于此答案的目的,假设您致电

navigator.serviceWorker.register('service-worker.js');

在您的页面上,这意味着您的 Service Worker JavaScript 资源是service-worker.js. 如果您不确定所使用的确切 Service Worker URL,请参见下文——可能是因为您向 Service Worker 脚本添加了哈希或版本信息。

问题归结为如何解决service-worker.js代码中的初始问题如果这是一个小错误修复,那么您显然可以进行更改并将其重新部署service-worker.js到您的托管环境。如果没有明显的bug修复和你不想离开你的用户运行马车服务人员的代码,而你花时间来制定出一个解决方案,这是一个好主意,让一个简单的,无操作 service-worker.js得心应手,就像下列的:

// A simple, no-op service worker that takes immediate control.

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.js需要包含的所有内容。因为没有fetch注册处理程序,来自受控页面的所有导航和资源请求最终将直接针对网络,有效地为您提供与根本没有 Service Worker 时相同的行为。

附加步骤

可以更进一步,强行删除使用Cache Storage API存储的所有内容,或者明确地完全取消注册 Service Worker对于大多数常见情况,这可能会有点矫枉过正,遵循上述建议应该足以让您处于当前用户获得预期行为的状态,并且一旦您修复了错误,您就可以重新部署更新. 即使是启动一个无操作的 Service Worker 也有一定程度的开销,所以如果你没有计划重新部署有意义的 Service Worker 代码,你可以走取消注册 Service Worker的路线

如果您已经在service-worker.js使用 HTTP 缓存指令为其提供服务的情况下,它的生命周期比您的用户可以等待的时间长,请记住,桌面浏览器上Shift + Reload将强制页面在外部重新加载服务工作者控制。并非每个用户都知道如何执行此操作,但在移动设备上也不可能。所以不要依赖 Shift + Reload 作为可行的回滚计划。

如果您不知道 Service Worker URL 怎么办?

上面的信息假定你知道什么服务工作者URL是- service-worker.jssw.js或别的东西的有效不变。但是,如果您在 Service Worker 脚本中包含某种版本控制或哈希信息,例如service-worker.abcd1234.js?

首先,在未来尽量避免这种情况——这是违反最佳实践的但是,如果您已经部署了许多版本化的 Service Worker URL,并且您需要为所有用户禁用某些功能,无论他们注册了哪个 URL,都有一个出路。

每次浏览器向 Service Worker 脚本发出请求时,无论是初始注册还是更新检查,它都会设置一个名为Service-Worker:.

假设您可以完全控制后端 HTTP 服务器,您可以检查传入请求是否存在此Service-Worker:标头,并始终以您的无操作服务工作者脚本响应进行响应,无论请求 URL 是什么。

配置您的 Web 服务器以执行此操作的具体细节因服务器而异。

Clear-Site-Data: 响应头

最后一点:当特殊的 HTTP 响应标头作为任何响应的一部分返回时,某些浏览器会自动清除特定数据并可能取消注册服务工作者:Clear-Site-Data:

尝试从错误的 Service Worker 部署中恢复时,设置此标头会很有帮助,并且功能规范中包含终止开关场景作为示例用例。

检查是很重要的浏览器支持的故事Clear-Site-Data:之前你依靠纯粹它作为一个杀死开关。截至 2019 年 7 月,支持 Service Worker 的浏览器并非 100% 都支持它,因此目前,Clear-Site-Data:如果您担心在所有浏览器中从有故障的 Service Worker 中恢复,与上述技术一起使用是最安全的

2021-03-23 20:52:11

您可以使用 javascript 来“取消注册”服务工作者。下面是一个例子:

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.getRegistrations().then(function (registrations) {
    //returns installed service workers
    if (registrations.length) {
      for(let registration of registrations) {
        registration.unregister();
      }
    }
  });
}
这必须放在.registe 方法之前,以便它会在注册之前取消注册,或者可以在任何地方运行?
2021-04-11 20:52:11

这是一个非常糟糕的情况,希望不会发生在你的生产中。

在这种情况下,如果您不想通过不同浏览器的开发人员工具,chrome://serviceworker-internals/对于基于闪烁的浏览器,或者about:serviceworkersabout:debugging#workers将来)在 Firefox 中,我会想到两件事:

  1. 使用 serviceworker 更新机制。您的用户代理将检查注册的工人是否有任何更改,将获取它并activate再次通过该阶段。因此,您可能可以更改 serviceworker 脚本,修复(清除缓存等)任何奇怪的情况并继续工作。唯一的缺点是您需要等到浏览器更新可能需要 1 天的时间。
  2. 为您的工作人员添加某种终止开关有一个特殊的 url,您可以在其中指向用户访问它可以恢复缓存的状态等。

我不确定清除浏览器数据是否会删除工作人员,因此这可能是另一种选择。

谢谢你的回答。选项 1 对于实时网站来说是负担不起的。我将看看选项 2,但如果您在您的网站顶级 ("/") 上注册您的服务工作者,我不太有信心它会起作用
2021-04-01 20:52:11

我还没有测试过这个,但是ServiceWorkerRegistration 对象上有一个unregister()和一个update()方法。你可以从 navigator.serviceWorker 得到这个。

navigator.serviceWorker.getRegistration('/').then(function(registration) {
  registration.update();
});

然后更新应立即检查是否有新的 serviceworker,如果有,请安装它。这绕过了 24 小时的等待期,每次遇到这个 javascript 时都会下载 serviceworker.js。

对于实时情况,您需要在字节级别更改 Service Worker(例如,在第一行添加注释),它将在接下来的 24 小时内更新您可以通过单击更新按钮在 Chrome 中使用 chrome://serviceworker-internals/ 模拟这一点

即使在 Service Worker 本身被缓存的情况下,这也应该有效,因为更新算法的第 9 步设置了一个标志来绕过 Service Worker。