如何:ServiceWorker 检查是否准备好更新

17

我想要实现的目标:

  • 使用加载器/旋转器渲染页面

  • 如果已经注册并激活了service-worker.js,则检查是否有更新

    • 如果没有更新,则移除加载器
    • 如果发现新版本updatefound,则重新加载页面
  • 否则注册service-worker.js
    • updatefound,即安装了新的版本时,移除加载器

我正在使用sw-precache模块来生成service-worker.js,并遵循以下注册代码:

window.addEventListener('load', function() {

  // show loader
  addLoader();

  navigator.serviceWorker.register('service-worker.js')
    .then(function(swRegistration) {

      // react to changes in `service-worker.js`
      swRegistration.onupdatefound = function() {
        var installingWorker = swRegistration.installing;
        installingWorker.onstatechange = function() {
          if(installingWorker.state === 'installed' && navigator.serviceWorker.controller){

            // updated content installed
            window.location.reload();
          } else if (installingWorker.state === 'installed'){

            // new sw registered and content cached
            removeLoader();
          }
        };
      }

      if(swRegistration.active){
        // here I know that `service-worker.js` was already installed
        // but not sure it there are changes
        // If there are no changes it is the last thing I can check
        // AFAIK no events are fired afterwards
      }

    })
    .catch(function(e) {
      console.error('Error during service worker registration:', e);
    });
});

阅读规范后,我发现似乎没有像updatenotfound这样的处理程序。看起来serviceWorker.register会通过运行get-newest-worker-algorithm检查service-worker.js是否在内部发生了更改,但我没有看到类似的方法通过公共API暴露出来。

我认为我的选项是:

  • 等待服务工作者注册变为活动状态后几秒钟,以查看是否触发了onupdatefound
  • 如果未更新缓存,则从service-worker.js代码中触发自定义事件

还有其他建议吗?


编辑:

我想出了一些代码,通过在SW注册和SW客户端之间使用postMessage()解决了这个问题(如@pate建议的那样)

以下演示试图通过postMessage在SW客户端和SW注册之间进行检查,但由于SW代码已被缓存,因此失败DEMO


编辑:

到目前为止,看起来我不能实现我想要的,因为:

  1. 当服务工作者处于活动状态时,您无法通过在SW中评估某些代码来检查更新-这仍然是相同的缓存SW,没有任何更改。
  2. 您需要等待onupdatefound,没有其他可以通知SW更改的方式了
  3. onupdatefound更老的SW的activation会先发生
  4. 如果没有变化,则在activation之后不会触发任何内容
  5. SW注册update()还不成熟,一直在变化,从Chrome 46开始,如果操作成功或没有更新,则update()会返回一个解析为'undefined'的承诺
  6. 设置超时以延迟视图渲染是次优的,因为没有明确的答案可以设置多长时间,它也取决于SW大小

1
你好,这个有什么更新吗? - Samuel Maisonneuve
实际上我不知道,生产环境中的应用程序在初始加载后(视图已呈现)会进行疯狂的刷新,并随后检测到更改。 - Ivar
好的,谢谢你的回答。 - Samuel Maisonneuve
难以置信,整整一天都试了,完全相同的用例。你最终实现了一个计时器吗?我在考虑这个问题,大多数应用程序都有几秒钟的加载屏幕。我猜也可以通过使用会话存储来禁用它。但对我来说仍然不可靠。 - maxischl
@maxischl 这是“一段时间”之前的事情了。我不确定 API 是否仍然相同,或者是否有所改进。 - Ivar
2个回答

4
另一个回答由Fabio提供,不起作用。Service Worker脚本无法访问DOM。无法从Service Worker内部删除任何DOM元素或操纵处理DOM元素的任何数据。该脚本独立运行,没有共享状态。但是,您可以在实际运行JS和Service Worker之间发送消息。我不确定这是否是完成OP要求的最佳方式,但可用于实现它。
1. 在页面上注册onmessage处理程序 2. 从SW的activate或install事件向页面发送消息 3. 在页面收到消息时采取相应措施
我自己在SW内部的变量中保留了SW版本号。然后,我的SW将该版本号发布到页面,并且页面将其存储到浏览器的localStorage中。下次加载页面时,SW将其当前版本号发布到页面,而onmessage处理程序将其与当前存储的版本号进行比较。如果它们不同,则SW已更新为包含在消息中的某个版本号。之后,我更新了版本号的localStorage副本并执行了此操作和那些操作。
此流程也可以反过来完成:从页面发送消息到SW,让SW回答一些内容,然后采取相应措施。
希望我能清楚地解释我的想法 :)

好的建议,我已经用一个hack过的解决方案做了一个plunker演示plunker demo。现在先等待更多的回复。 - Ivar
是的,我并没有声称提供完整的过程,只是提供了概念。当我在 SW 脚本中写下 removeLoader(); 时,我的意思是在那里必须要做一些事情(一个消息?一个事件检查?)。 - Fabio Marzocca
一个需要注意的地方是,如果软件尚未注册(首次安装),那么这是可以接受的,但如果已经注册,则事件将从已经处于活动状态的软件中触发,并且它会说没有任何更改。因此,似乎您无法保证在收到这些内容后不会触发“onupdatefound”。 - Ivar

0
我脑海中唯一想到的方法是正常启动加载器,然后在 service-worker 的 install 函数中将其移除。我会尝试这个方法,在你的 service-worker.js 文件中:
self.addEventListener('install', function(e) {
  console.log('[ServiceWorker] Install');
  e.waitUntil(
    caches.open(cacheName).then(function(cache) {
      console.log('[ServiceWorker] Caching app shell');
      return cache.addAll(filesToCache);
    }).then(function() {
        ***removeLoader();***
        return self.skipWaiting();
    })
  );
});

它看起来应该解决一个问题,但只有在 worker 更改时才会触发此事件。https://w3c.github.io/ServiceWorker/#service-worker-global-scope-install-event - Ivar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接