16

我想要达到的目标:

  • 使用加载器/微调器渲染页面

  • 如果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. 看起来像 通过运行get-newest-worker-algorithmserviceWorker.register检查是否在service-worker.js内部进行了更改,但我看不到通过公共 api 公开的类似方法。

我认为我的选择是:

  • 在服务工作者注册激活后等待几秒钟,看看是否onupdatefound被解雇
  • service-worker.js如果缓存未更新,则从代码中触发自定义事件

还有其他建议吗?


编辑:

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

下面的demo尝试通过软件客户端和软件注册之间的检查来实现postMessage,但是由于软件代码已经被缓存而失败了DEMO


编辑:

所以现在看来​​我无法实现我想要的,因为:

  1. 当服务人员处于活动状态时,您无法通过评估 SW 中的某些代码来检查更新 - 这仍然是相同的缓存 SW,没有更改
  2. 你需要等待onupdatefound,没有其他东西会通知 SW 的变化
  3. activation较旧的 SW 出现在之前onupdatefound
  4. 如果没有变化,则不会触发activation
  5. SW注册update()不成熟,不断变化,Starting with Chrome 46, update() returns a promise that resolves with 'undefined' if the operation completed successfully or there was no update
  6. 设置超时以推迟视图渲染是次优的,因为对于应该设置多长时间没有明确的答案,它也取决于 SW 大小
4

2 回答 2

4

法比奥提供的另一个答案不起作用。Service Worker 脚本无权访问 DOM。不可能从 DOM 中删除任何内容,或者,例如,无法从Service Worker 内部操作处理 DOM 元素的任何数据。该脚本单独运行,没有共享状态。

但是,您可以做的是在实际运行页面的 JS 和 Service Worker 之间发送消息。我不确定这是否是执行 OP 要求的最佳方法,但可以用来实现它。

  1. 在页面上注册一个 onmessage 处理程序
  2. 从 SW 的激活或安装事件向页面发送消息
  3. 当页面上收到消息时采取相应的行动

我自己将 SW 版本号保存在 SW 内部的一个变量中。然后,我的 SW 将该版本号发布到页面,页面将其存储到浏览器的 localStorage 中。下次加载页面时,SW 将其当前版本号发布到页面,并且 onmessage 处理程序将其与当前存储的版本号进行比较。如果它们不相同,则 SW 已更新为包含在 mssage 中的某个版本号。之后,我更新了版本号的 localStorage 副本并完成了这个和那个。

这个流程也可以通过其他方式完成:从页面向 SW 发送消息,让 SW 回复一些内容,然后采取相应的行动。

我希望我能够清楚地解释我的想法:)

于 2017-08-20T19:10:02.843 回答
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();
    })
  );
});
于 2017-08-20T09:41:48.830 回答