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裏垃圾不清掉,它就會一直留在那裏了。這麼清post

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接管已經打開但沒受到控制的瀏覽器標籤頁面。性能

skipWaiting的使用場景是在sw更新時,由於有上一個sw正在控制着全部該站點的頁面,新的sw在active後進入waiting狀態,直到用戶將全部該站點頁面關閉,新的sw才上位。這跟Chrome和VScode的更新機制同樣,在使用過程當中有更新的時候,並不影響你繼續使用老版本,而是在重啓程序後,直接才變成新版。經過skipWaiting方法,能夠直接讓waiting狀態的新sw替換掉老的sw,注意 還會自動接管上一個sw管轄的頁面測試

我是不推薦使用clients.claim的,首先出現不受控標籤的狀況相對比較少,何況首次加載速度尤爲重要,能省點開銷就省點吧。fetch

在sw裏監聽fetch事件,請求多過了一層sw,會有性能損耗嗎?

固然會,像下面這樣搞,是萬萬不要的ui

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接管了頁面,這樣就能夠收到');
      })
    });
})
複製代碼
相關文章
相關標籤/搜索