Service Worker 有时无需等待即可安装

Service Worker sometimes installs without waiting

我在我的 Angular 应用程序中使用 Workbox,我每 15 分钟检查一次更新,以便在部署新版本时通知用户。

找到更新后,新的 Service Worker 已安装但未激活,因为此时我可以提示用户重新加载页面:

点击确定后,我可以通知我的SW跳过等待,因为他有这个EventListener:

addEventListener('message', (event) => {
  if (event.data && event.data.type === 'SKIP_WAITING') {
    skipWaiting();
  }
});

这很好用,但有时(我还没弄明白为什么)新软件会立即安装并激活而无需等待(即使没有收到消息)已发送)。

怎么会这样?

我实现了 2 种方法来检查更新并做出相应的反应,两者都会导致同样的问题。

第一种,来自官方Docs:

public async registerServiceWorker() {
  this.workbox = new Workbox('/sw.js', {});
  try {
    this.workbox.addEventListener('waiting', () => promptTheUser());
    this.swRegistration = await this.workbox.register();
  } catch (e) {
    console.log('[SW] error registering service worker', e);
  }    
}

public async checkForUpdate() {
    return await this.swRegistration.update();
}

public activateNow() { // Called in the user prompt
    this.workbox.addEventListener('controlling', (event) => {
      window.location.reload();
    });
    if (this.swRegistration && this.swRegistration.waiting) {
      messageSW(this.swRegistration.waiting, { type: 'SKIP_WAITING' });
    }
}

第二种方式,使用ServiceWorkerRegistration.onupdatefound and ServiceWorkerContainer.oncontrollerchange的一种:

public async registerServiceWorker() {
    this.workbox = new Workbox('/sw.js', {});
    this.swRegistration = await this.workbox.register();
    this.swRegistration.installing.addEventListener('statechange', () => {
      if (this.swRegistration.waiting) {
        promptTheUser();
      }
    });
}

public async checkForUpdate() {
    return await this.swRegistration.update();
}

public activateNow() {
    navigator.serviceWorker.addEventListener('controllerchange', () => {
      window.location.reload();
    });
    if (this.swRegistration && this.swRegistration.waiting) {
      messageSW(this.swRegistration.waiting, { type: 'SKIP_WAITING' });
    }
}

在应用程序启动时调用 registerServiceWorker() 方法。 这两种方法应该产生相同的结果,但是如果未达到“控制”状态,它们将不起作用。相反,新的软件只是安装并激活。

结果,页面从未重新加载,我的用户提示保持打开状态...

有什么想法吗?

好的,我找到问题了。 “controllerchange”事件没有被触发的原因是 在第一次安装 service worker 时,如果页面没有重新加载,客户端就不会被认领。我之所以发现这个是因为在绝望中我阅读了 Workbox.ts 的源代码并偶然发现了这个:

Note: the first time a service worker is installed it will active but not start controlling the page unless clients.claim() is called in the service worker.

对我来说,这是一条非常重要的信息,但在文档(或可以在网上找到的各种状态图)中没有足够突出显示。

所以我通过在第一次安装后调用 clients.claim() 解决了我的问题(这样它对用户是透明的),所有后续更新都会触发用户重新加载页面的通知。

希望这可以帮助 运行 遇到此问题的其他人