刷新时激活更新的服务工作者

和法布利希:

当服务工作者进行更新时,它不会以正确的方式控制页面。它进入“等待”状态,等待被激活。

令人惊讶的是,更新的服务程序甚至在刷新页面后都无法控制选项卡。Google解释:

https://developers.google.com/web/fundamentals/instant-and-offline/service-worker/lifecycle

即使您只打开一个选项卡演示,刷新页面也不足以让新版本接管。这是由于浏览器导航的工作原理。导航时,直到收到响应标头后,当前页面才会消失,即使响应包含Content-Disposition标头,当前页面也可能会停留由于此重叠,当前的服务工作者始终在刷新期间控制客户端。

要获取更新,请使用当前服务工作程序关闭或导航至所有选项卡。然后,当您再次导航至演示时,您应该会看到这匹马[更新的内容]。

此模式类似于Chrome更新的方式。Chrome的更新会在后台下载,但要等到Chrome重新启动后才能应用。同时,您可以继续使用当前版本而不会中断。但是,这在开发过程中是很痛苦的,但是DevTools有使它变得更容易的方法,我将在本文的后面介绍。

在多个选项卡的情况下,此行为是有意义的。我的应用程序是一个捆绑包,需要原子更新。我们无法混合和匹配旧捆绑包的一部分和新捆绑包的一部分。(在本机应用程序中,原子性是自动的并且有保证。)

但是在单个标签的情况下,我将其称为应用程序的“上次打开的标签”,这不是我想要的。我想刷新上一个打开的选项卡以更新我的服务人员。

(很难想象有人真的希望旧的服务人员在刷新最后一个打开的选项卡后继续运行。Google的“导航重叠”参数在我看来像是一个不幸的错误的很好的借口。)

我的应用程序通常仅在单个标签中使用。在生产中,我希望我的用户仅通过刷新页面就能使用最新代码,但是那将行不通:旧的服务工作者将在整个刷新期间保持控制权。

我不想告诉用户“要接收更新,请确保关闭或导航离开我的应用程序”。我想告诉他们,“只是刷新”。

用户刷新页面后,如何激活更新的服务人员?

编辑:我知道有一个答案是快速,简单和错误的:skipWaiting在service worker的install事件中。skipWaiting在旧页面标签打开的情况下,更新下载后,新服务工作者将立即生效。这使得更新不安全地成为非原子的;就像在应用程序运行时替换本机应用程序捆绑包一样。对我来说那不好。我需要等到用户刷新最后打开的选项卡的页面。

和法布利希:

我写了一篇博客文章,解释了如何处理。https://redfin.engineering/how-to-fix-the-refresh-button-when-using-service-workers-a8e27af6df68

我在github上也有一个工作示例。https://github.com/dfabulich/service-worker-refresh-sample

有四种方法:

  1. skipWaiting()在安装上。这很危险,因为您的标签可以同时包含新旧内容。
  2. skipWaiting()安装时,并刷新“controllerchange更好” 上的所有打开的选项卡,但是当选项卡随机刷新时,您的用户可能会感到惊讶。
  3. 刷新上所有打开的选项卡controllerchange安装时,请skipWaiting()通过应用内“刷新”按钮提示用户甚至更好,但是用户必须使用应用程序内的“刷新”按钮;浏览器的刷新按钮仍然无法使用。这在Google关于服务工作者的Udacity课程和《Workbox高级指南》以及master在Github上的示例分支中都有详细记录
  4. 如果可能,请刷新最后一个标签。在以navigate模式进行获取期间,计算打开的选项卡(“客户端”)。如果只有一个打开的选项卡,则skipWaiting()立即并通过返回带有Refresh: 0标题的空白响应来刷新唯一的选项卡您可以在Github上的示例refresh-last-tab分支中看到一个有效的示例

全部放在一起...

在服务人员中:

addEventListener('message', messageEvent => {
  if (messageEvent.data === 'skipWaiting') return skipWaiting();
});

addEventListener('fetch', event => {
  event.respondWith((async () => {
    if (event.request.mode === "navigate" &&
      event.request.method === "GET" &&
      registration.waiting &&
      (await clients.matchAll()).length < 2
    ) {
      registration.waiting.postMessage('skipWaiting');
      return new Response("", {headers: {"Refresh": "0"}});
    }
    return await caches.match(event.request) ||
      fetch(event.request);
  })());
});

在页面中:

function listenForWaitingServiceWorker(reg, callback) {
  function awaitStateChange() {
    reg.installing.addEventListener('statechange', function() {
      if (this.state === 'installed') callback(reg);
    });
  }
  if (!reg) return;
  if (reg.waiting) return callback(reg);
  if (reg.installing) awaitStateChange();
  reg.addEventListener('updatefound', awaitStateChange);
}

// reload once when the new Service Worker starts activating
var refreshing;
navigator.serviceWorker.addEventListener('controllerchange',
  function() {
    if (refreshing) return;
    refreshing = true;
    window.location.reload();
  }
);
function promptUserToRefresh(reg) {
  // this is just an example
  // don't use window.confirm in real life; it's terrible
  if (window.confirm("New version available! OK to refresh?")) {
    reg.waiting.postMessage('skipWaiting');
  }
}
listenForWaitingServiceWorker(reg, promptUserToRefresh);

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

如何在服务工作者中刷新图片

PWA服务工作者缓存/更新预期行为

如何由服务工作者更新缓存的资源

从Create React App注册服务工作者时如何更新页面

离线时服务工作者未注册

注销时如何清除服务工作者缓存?

服务工作者缓存与 VuexPersistence

如何卸载服务工作者?

notificationclick事件服务工作者

服务工作者中的XMLHttpRequest

ServiceWorkerRegistration.update()是否跳过等待阶段并激活新的服务工作者?

PWA-服务工作者(反应)如何工作?

服务工作者不在离线工作

服务工作者,它是如何工作的?

当 firebase 配置发生变化时更新 firebase 消息服务工作者

Javascript服务工作者:从缓存中获取资源,但也进行更新

如何在Google Devtools中强制更新服务工作者文件?

用户在网站上时,可以使用服务工作者显示通知吗?

使用CSRF令牌缓存请求时,如何减少服务工作者缓存的大小

通过http的服务工作者:获取脚本时发生SSL证书错误

当多个服务工作者被注册时,navigator.serviceWorker

服务工作者在第一次加载时获取事件

服务工作者在浏览器离线时保存表单数据

在线时影响非 GET 请求的服务工作者 - @angular/pwa

如何使用服务工作者预缓存名称在每次部署时都会更改的资产?

服务工作者在反应中缓存的路由

WebView:以编程方式清除服务工作者缓存

未找到reactjs服务工作者.license文件

Firebase Web推送通知服务工作者问题