ServiceWorkerPWA的核心在于ServiceWorker。目前中文社区对ServiceWorker的知识深度普遍不足,难以处理实际项目中的问题。比如我想知道卸载sw(以下简称sw)后是否需要手动清理缓存,搜索引擎也没有很好的答案。本文结合淘宝首页PWA的经验,分享我认为非常有价值的关于ServiceWorker的知识。先从注册说起,什么时候注册sw呢?有些教程这样注册swif('serviceWorker'innavigator){navigator.serviceWorker.register('/sw.js')}这样做会导致第一个问题,sw线程会增加CPU和内存的使用,而且,sw中预缓存的资源需要下载,移动设备带宽有限。当sw线程被占用时,主进程的带宽就变成了一条小水管。第一次打开各种资源是很有价值的,而且是循序渐进的,所以第一次打开页面的时候不需要去争缓存资源。正确的做法是在页面加载完成后进行sw。if('serviceWorker'innavigator){window.addEventListener('load',function(){navigator.serviceWorker.register('/sw.js');});}我想注销所有注册用户sw,如何最安全的方法是什么?并非所有的移动浏览器都支持getRegistrations,getRegistration更可靠。可以先尝试使用getRegistrations,失败后再尝试getRegistration,如下。varserviceWorker=navigator.serviceWorker;serviceWorker.getRegistrations?serviceWorker.getRegistrations().then(function(sws){sws.forEach(function(sw){sw.unregister();});}):serviceWorker.serviceRegistration&&Worker.getRegistration().then(function(sw){sw&&sw.unregister();});我退出了sw,是否需要处理之前留下的缓存?是的,虽然cacheStorage属于PWA规范API,但它是独立的。虽然serviceworker被取消,缓存中的垃圾无法清除,但它会永远存在。所以清除window.caches&&caches.keys&&caches.keys().then(function(keys){keys.forEach(function(key){caches.delete(key);});});应该使用self.clients.claim吗?clients.claim的作用是让当前SW接管所有已经打开的tab。使用场景是当用户第一次打开注册sw的页面时,仍然有同域页面的其他浏览器标签页。之前打开的页面没有被接管,所以通过clients.claim接管打开但不受控制的浏览器标签页。skipWaiting的使用场景是sw更新的时候,因为之前的sw控制着站点的所有页面,新sw激活后进入waiting状态,直到用户关闭所有的sw才会接管网站的页面。这一点和Chrome、VScode的更新机制是一样的。使用过程中有更新时,不影响您继续使用旧版本,重启程序后直接变成新版本。通过skipWaiting方法可以在waiting状态下直接用新sw替换旧sw,注意会自动接管之前sw所管辖的页面。我不推荐使用clients.claim。首先,不受控标签的情况相对较少。此外,首次加载速度尤为重要。如果可以节省一些开销,请节省一些。监听sw中的fetch事件,如果request超过sw一层,会不会有性能损失?当然,完全没有必要像下面这样self.addEventListener('fetch',event=>{event.respondWith(fetch(event.request));});为什么我在sw中postMessage到页面,页面收不到消息这是在测试serviceWorker的postMessage能力时经常遇到的问题。要想找到原因,就要从sw接管的页面入手。使用sw.js中的self.clients.matchAll方法获取当前serviceWorker实例接管的所有选项卡。注意当前实例已经接管,sw.js中的代码只会执行一次。下面的代码是self.clients.matchAll().then(function(clients){clients.forEach(client=>{client.postMessage('Thismessagewillnotbereceived');})});clients必须是一个一个空数组,因此postMessage永远不会到达页面。第一次安装后如何将消息发布到页面?答案是self.skipWaiting,然后在activate事件中使用self.clients.matchAll,因为调用了skipWaiting,所以当前sw会在install后立即激活并接管之前sw的所有tab,这样就可以搞定了在新的sw中进入标签页postMessageself.skipWaiting()self.addEventListener('activate',()=>{self.clients.matchAll().then(function(clients){clients.forEach(client=>{client.postMessage('skipWaiting让新的sw接管页面以便它可以接收');})});})
