刷新时激活更新的 Service Worker [英] Activate updated service worker on refresh

查看:31
本文介绍了刷新时激活更新的 Service Worker的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当 Service Worker 更新时,它不会以正确的方式控制页面;它进入等待"状态,等待被激活.

When a service worker updates, it doesn't take control of the page right way; it goes into a "waiting" state, waiting to be activated.

令人惊讶的是,更新后的 Service Worker 在刷新页面后甚至没有控制选项卡.谷歌解释:

Surprisingly, the updated service worker doesn't even take control of the tab after refreshing the page. Google explains:

https://developers.google.com/网络/基础知识/即时和离线/服务工作者/生命周期

即使你只打开了一个选项卡进入演示,刷新页面不足以让新版本接管.这是由于如何浏览器导航工作.导航时,当前页面不会离开,直到收到响应标头,即使如此如果响应具有 Content-Disposition,则当前页面可能会停留标题.由于这种重叠,当前的服务工作者总是在刷新期间控制客户端.

Even if you only have one tab open to the demo, refreshing the page isn't enough to let the new version take over. This is due to how browser navigations work. When you navigate, the current page doesn't go away until the response headers have been received, and even then the current page may stay if the response has a Content-Disposition header. Because of this overlap, the current service worker is always controlling a client during a refresh.

要获取更新,请使用当前服务人员.然后,当您再次导航到演示时,您应该看到马[更新内容].

To get the update, close or navigate away from all tabs using the current service worker. Then, when you navigate to the demo again, you should see the horse [updated content].

这种模式类似于 Chrome 的更新方式.Chrome 更新在后台下载,但在 Chrome 重新启动之前不要应用.在同时,您可以继续使用当前版本而无需中断.然而,这是开发过程中的痛苦,但 DevTools有一些方法可以使它更容易,我将在本文后面介绍.

This pattern is similar to how Chrome updates. Updates to Chrome download in the background, but don't apply until Chrome restarts. In the mean time, you can continue to use the current version without disruption. However, this is a pain during development, but DevTools has ways to make it easier, which I'll cover later in this article.

这种行为在多个标签的情况下是有意义的.我的应用程序是一个需要原子更新的包;我们不能混合和匹配旧包的一部分和新包的一部分.(在原生应用中,原子性是自动且有保证的.)

This behavior makes sense in the case of multiple tabs. My app is a bundle that needs to be updated atomically; we can't mix and match part of the old bundle and part of the new bundle. (In a native app, atomicity is automatic and guaranteed.)

但在单个选项卡的情况下,我将其称为应用程序的最后打开的选项卡",这种行为不是我想要的.我想刷新最后一个打开的选项卡以更新我的服务工作者.

But in the case of a single tab, which I'll call the "last open tab" of the app, this behavior is not what I want. I want refreshing the last open tab to update my service worker.

(很难想象有人真的希望旧的 Service Worker 在最后一个打开的选项卡刷新时继续运行.Google 的导航重叠"论点在我看来是一个很好的借口错误.)

(It's hard to imagine that anybody actually wants the old service worker to continue running when the last open tab is refreshed. Google's "navigation overlap" argument sounds to me like a good excuse for an unfortunate bug.)

我的应用通常只在单个标签中使用.在生产中,我希望我的用户能够通过刷新页面来使用最新的代码,但这不起作用:旧的 Service Worker 将在刷新时保持控制.

My app is normally only used in a single tab. In production, I want my users to be able to use the latest code just by refreshing the page, but that won't work: the old service worker will remain in control across refreshes.

我不想告诉用户,要接收更新,请务必关闭或离开我的应用."我想告诉他们,刷新一下."

I don't want to have to tell users, "to receive updates, be sure to close or navigate away from my app." I want to tell them, "just refresh."

如何在用户刷新页面时激活更新后的 Service Worker?

How can I activate my updated service worker when the user refreshes the page?

我知道有一个答案是快速、简单和错误的:skipWaiting 在服务工作者的安装事件中.skipWaiting 将使新的 Service Worker 在更新下载后立即生效,同时旧页面选项卡处于打开状态.这使得更新不安全地非原子性;这就像在应用程序运行时替换本机应用程序包.这对我来说不好.我需要等到用户刷新最后打开的标签页.

There's one answer I'm aware of that's quick, easy, and wrong: skipWaiting in the service worker's install event. skipWaiting will make the new service worker take effect as soon as the update downloads, while the old page tab is open. That makes the update unsafely nonatomic; it's like replacing a native app bundle while the app's running. That's not OK for me. I need to wait until the user refreshes the page of the last open tab.

推荐答案

我写了一篇博文解释了如何处理这个问题.https://redfin.engineering/how-to-fix-the-refresh-button-when-using-service-workers-a8e27af6df68

I wrote a blog post explaining how to handle this. https://redfin.engineering/how-to-fix-the-refresh-button-when-using-service-workers-a8e27af6df68

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

I also have a working sample on github. https://github.com/dfabulich/service-worker-refresh-sample

有四种方法:

  1. skipWaiting() 关于安装.这很危险,因为您的标签页可能会混合使用新旧内容.
  2. skipWaiting() 在安装时,并在 controllerchange 上刷新所有打开的标签页更好,但是当标签页随机刷新时,您的用户可能会感到惊讶.
  3. 刷新controllerchange上所有打开的标签;在安装时,使用应用内刷新"按钮提示用户 skipWaiting(). 更好,但用户必须使用应用内刷新"按钮;浏览器的刷新按钮仍然不起作用.这在 Google 的 Udacity Service Workers 课程Workbox 高级指南,以及我的 Github 示例 的 master 分支.
  4. 如果可能,请刷新最后一个标签.navigate 模式下提取期间,计算打开的标签(客户端").如果只有一个打开的选项卡,则 skipWaiting() 立即并通过返回带有 Refresh: 0 标头的空白响应来刷新唯一的选项卡.您可以在我的 Github 上的示例.
  1. skipWaiting() on installation. This is dangerous, because your tab can get a mix of old and new content.
  2. skipWaiting() on installation, and refresh all open tabs on controllerchange Better, but your users may be surprised when the tab randomly refreshes.
  3. Refresh all open tabs on controllerchange; on installation, prompt the user to skipWaiting() with an in-app "refresh" button. Even better, but the user has to use the in-app "refresh" button; the browser's refresh button still won't work. This is documented in detail in Google's Udacity course on Service Workers and the Workbox Advanced Guide, as well as the master branch of my sample on Github.
  4. Refresh the last tab if possible. During fetches in navigate mode, count open tabs ("clients"). If there's just one open tab, then skipWaiting() immediately and refresh the only tab by returning a blank response with a Refresh: 0 header. You can see a working example of this in the refresh-last-tab branch of my sample on Github.

综合起来...

在 Service Worker 中:

In the Service Worker:

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);

这篇关于刷新时激活更新的 Service Worker的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆