PWA进阶:Service Worker一问一答

PWA的核心在于Service Worker,目前中文社区中关于Service Worker的知识深度广泛不够,难以应对实际项目中的问题。例如我想要知道在卸载sw(下文简称sw)后需不须要手动清理caches,搜索引擎是没有什么好答案的。这篇文章结合淘宝首页PWA的经验,分享出我认为很是有价值的关于Service Worker的知识点。javascript

先从注册提及,sw应该在何时注册?

一些教程是这样注册sw的java

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/sw.js')
}
复制代码

这样作会形成第一个问题,sw线程将加重对CPU和内存的使用,而且sw内预缓存的资源是须要下载的,移动设备带宽有限,sw线程占用的同时,主进程带宽就变成了小水管了。数组

首次打开各类资源都很是宝贵,何况是渐进式,彻底没有必要争第一次打开页面就要缓存资源。正确的作法是,页面加载完之后sw的事。浏览器

if ('serviceWorker' in navigator) {
  window.addEventListener('load', function() {
    navigator.serviceWorker.register('/sw.js');
  });
}
复制代码

我想注销全部已注册用户的sw,怎么作才最稳妥?

并非全部移动端浏览器都支持getRegistrations,getRegistration更靠谱,能够先尝试使用getRegistrations,若是没法使用再尝试getRegistration,以下。缓存

var serviceWorker = navigator.serviceWorker;

serviceWorker.getRegistrations ? serviceWorker.getRegistrations().then(function(sws) {
  sws.forEach(function(sw) {
    sw.unregister();
  });
}) : serviceWorker.getRegistration && serviceWorker.getRegistration().then(function(sw) {
  sw && sw.unregister();
});
复制代码

我注销了sw,以前留下的caches还须要本身动手处理吗?

须要,cacheStorage虽然属于PWA规范API当中,但它是独立的,虽然注销了service worker,caches里垃圾不清掉,它就会一直留在那里了。这么清markdown

window.caches && caches.keys && caches.keys().then(function(keys) {
  keys.forEach(function(key) {
    caches.delete(key);
  });
});
复制代码

该不应使用self.clients.claim?

clients.claim的做用是使当前SW接管已经打开的全部标签页,使用场景是用户首次打开注册sw的页面时,还存在其余同域页面的浏览器标签的状况。以前打开的页面没有被接管,因此经过clients.claim接管已经打开但没受到控制的浏览器标签页面。post

skipWaiting的使用场景是在sw更新时,由于有上一个sw正在控制着全部该站点的页面,新的sw在active后进入waiting状态,直到用户将全部该站点页面关闭,新的sw才上位。这跟Chrome和VScode的更新机制同样,在使用过程当中有更新的时候,并不影响你继续使用老版本,而是在重启程序后,直接才变成新版。经过skipWaiting方法,能够直接让waiting状态的新sw替换掉老的sw,注意 还会自动接管上一个sw管辖的页面性能

我是不推荐使用clients.claim的,首先出现不受控标签的状况相对比较少,何况首次加载速度尤为重要,能省点开销就省点吧。测试

在sw里监听fetch事件,请求多过了一层sw,会有性能损耗吗?

固然会,像下面这样搞,是万万不要的fetch

self.addEventListener('fetch', event => {
  event.respondWith(fetch(event.request));
});
复制代码

为何我在sw中postMessage到页面,页面没法收到message

这是在测试serviceWorker的postMessage能力时,常常遇到的一个问题,想要找到缘由就要从sw接管的页面提及。在sw.js中使用self.clients.matchAll方法获取当前serviceWorker实例所接管的全部标签页,注意是当前实例 已经接管的,而且sw.js中的代码只会执行一次,当sw.js代码中存在以下代码时

self.clients.matchAll()
  .then(function (clients) {
    clients.forEach(client => {
      client.postMessage('这条消息不会被收到');
    })
  });
复制代码

clients必定是个空数组,因此永远也postMessage不到页面。那要如何才能在首次install就postMessage到页面上那?答案是self.skipWaiting,而后在activate事件中使用self.clients.matchAll,由于调用了skipWaiting,当前的sw在install之后会马上avtivate并接管上一个sw的全部标签页,这样就能在新sw中拿到标签页postMessage了

self.skipWaiting()
self.addEventListener('activate', () => {
  self.clients.matchAll()
    .then(function (clients) {
      clients.forEach(client => {
        client.postMessage('skipWaiting让新的sw接管了页面,这样就能够收到');
      })
    });
})
复制代码
相关文章
相关标签/搜索