如何思考服務工作者。
Service Worker 的力量強大,絕對值得學習。讓您為使用者提供全新等級的體驗。網站可以立即載入。可離線運作。這類應用程式可做為特定平台應用程式安裝,並提供精緻的使用體驗,同時享有網頁的觸及範圍和自由度。
但服務工作站與大多數網頁開發人員所熟悉的任何東西都不同。不僅容易學習,而且還多了幾隻苦事要小心。
Google Developers 與我最近合作推出 Service Workies 專案,這是讓服務人員瞭解服務工作的免費遊戲。在建構服務 worker 的複雜內部和外部作業時,我遇到了一些問題。對我來說,最有幫助的是提出一些具象比喻。在本篇文章中,我們將探討這些心智模型,並深入瞭解服務端代理程式既複雜又強大的矛盾特徵。
相同,但不同
編寫服務工作程式碼時,您會發現許多內容都很熟悉。您可以使用自己最喜歡的全新 JavaScript 語言功能。監聽生命週期事件的方式與 UI 事件相同。您可以使用承諾管理控制流程,就像以往一樣。
但其他服務工作程式行為會讓您一頭霧水。尤其是在重新整理頁面後,程式碼變更未套用時。
新圖層
通常在建構網站時,您只需要考慮兩個層面:用戶端和伺服器。服務工作者是位於中間的全新層級。
服務工作處理程序就像是瀏覽器擴充功能,一種可以在使用者的瀏覽器中安裝。安裝完成後,Service Worker 會使用強大的中層「擴充」網站的瀏覽器。這個服務工作架構層可攔截並處理網站發出的所有要求。
服務工作者層有自己的生命週期,不受瀏覽器分頁影響。只重新整理網頁並不足以更新 Service Worker,只是您並不希望重新整理頁面來更新部署在伺服器上的程式碼。每個圖層都有各自的更新規則。
在 Service Workies 遊戲中,我們涵蓋了 Service Worker 生命週期的許多詳細資料,我們會針對這個生命週期提供一些實務做法。
功能強大,但功能有限
在網站上使用 Service Worker 可帶來許多好處。你的網站可以:
雖然服務工作程式功能多元,但設計上仍有限制。他們無法執行任何同步作業,也無法在與您的網站相同的執行緒中執行作業。這表示無法存取以下項目:
- localStorage
- DOM
- 窗戶
好消息是,您的網頁可透過多種方式與服務工作人員溝通,包括直接 postMessage
、一對一訊息管道,以及一對多廣播頻道。
長壽,但壽命短
即使使用者離開網站或關閉分頁,仍會保留服務工作程式。瀏覽器會保留這個 Service Worker,以便在使用者下次造訪網站時使用。在首次要求發出之前,服務工作者有機會攔截該要求並控制網頁。這就是網站可以離線運作的原因,就算使用者沒有連上網際網路,服務工作人員也可以提供網頁本身的快取版本。
在服務工作程式中,我們透過 Kolohe (友善的服務工作程式) 攔截及處理要求,以圖像化方式呈現這個概念。
已停止
儘管服務工作處理程序看似不重要,但他們幾乎隨時都可能停止。瀏覽器不想在目前沒有任何動作的 Service Worker 上浪費資源。停止與終止不同,服務工作者仍會安裝並啟用。只是讓裝置進入休眠狀態。下次需要時 (例如處理要求),瀏覽器就會喚醒該服務。
waitUntil
由於服務工作者隨時都有可能處於休眠狀態,因此需要透過某種方式讓瀏覽器知道何時正在執行重要作業,而不需要休眠。這時 event.waitUntil()
就能派上用場。這個方法會延長生命週期,讓生命週期不會在未準備就緒前停止或進入下個階段。這樣一來,我們就能有時間設定快取、從網路擷取資源等。
這個範例會告訴瀏覽器,在建立 assets
快取並填入劍的圖片之前,服務工作站的安裝作業不會完成:
self.addEventListener("install", event => {
event.waitUntil(
caches.open("assets").then(cache => {
return cache.addAll(["/weapons/sword/blade.png"]);
})
);
});
留意全球狀態
這個開始/停止作業發生時,系統會重設 Service Worker 的全域範圍。因此,請務必避免在服務工作者中使用任何全域狀態,否則下次服務 worker 喚醒時,如果狀態與預期不同,您將會感到失望。
請參考使用全域狀態的範例:
const favoriteNumber = Math.random();
let hasHandledARequest = false;
self.addEventListener("fetch", event => {
console.log(favoriteNumber);
console.log(hasHandledARequest);
hasHandledARequest = true;
});
這個 Service Worker 會在每次要求時記錄一個數字—假設 0.13981866382421893
。hasHandledARequest
變數也會變更為 true
。服務工作站現在處於閒置狀態,因此瀏覽器會停止它。下次收到要求時,服務工作處理程序必須再次,瀏覽器才會喚醒工作。系統會重新評估指令碼指令碼。hasHandledARequest
會重設為 false
,而 favoriteNumber
則會變成完全不同的 0.5907281835659033
。
您無法依賴服務工作站中的儲存狀態。此外,建立訊息管道等項目的例項可能會導致錯誤:每次服務工作程式停止/啟動時,您都會取得全新的例項。
在第 3 章「Service Worker」中,我們將停止的 Service Worker 視為在等待喚醒時失去所有顏色。
相輔相成,
您的網頁一次只能由一個 Service Worker 控管。但可以同時安裝兩個服務工作者。當您變更服務工作者程式碼並重新整理網頁時,您實際上並未編輯服務工作者。服務工作者無法變更。而是建立全新的帳戶。這個新的服務工作者 (我們稱之為 SW2) 會安裝,但不會啟用。必須「等待」目前的 Service Worker (SW1) 終止 (使用者離開您的網站時)。
干擾其他服務工作程的快取
安裝期間,SW2 可以完成設定,通常是建立並填入快取。不過請注意,這個新的服務工作程式可存取目前服務工作程式可存取的所有內容。如果你不小心,新的等待中 Service Worker 可能會對目前的 Service Worker 造成嚴重影響。以下列舉幾個可能會導致你遇到問題的情況:
- SW2 可能會刪除 SW1 正在使用的快取。
- SW2 可以編輯 SW1 使用的快取內容,導致 SW1 回應該網頁預期之外的資產。
略過等待等待
Service Worker 也可以使用風險較高的 skipWaiting()
方法,在安裝完成後立即控管網頁。除非您有意嘗試更換故障的 Service Worker,否則這通常不是問題。新的服務工作者可能會使用目前網頁未預期的更新資源,導致錯誤和錯誤。
清理起步
要避免服務工作處理程序彼此同步,方法就是確保他們使用不同的快取。最簡單的方法就是為所使用的快取名稱建立版本。
const version = 1;
const assetCacheName = `assets-${version}`;
self.addEventListener("install", event => {
caches.open(assetCacheName).then(cache => {
// confidently do stuff with your very own cache
});
});
部署新的 Service Worker 時,您將串場 version
,使其使用與上一個 Service Worker 之間完全獨立的快取,執行需要的工作。
結束清理
服務工作者達到 activated
狀態後,您就知道它已接管,而先前的服務工作者已不必要。此時,請務必清理舊服務工作程式。這不僅能尊重使用者的快取儲存空間限制,還能避免不經意產生的錯誤。
caches.match()
方法是常用的捷徑,可從任何快取中擷取相符的項目。但會依建立順序逐一檢查快取。假設您在兩個不同的快取 (assets-1
和 assets-2
) 中,有兩個版本的指令碼檔案 app.js
。您的網頁預期會使用儲存在 assets-2
中的新版指令碼。不過,如果你沒有刪除舊的快取,caches.match('app.js')
就會從 assets-1
傳回舊快取,這很可能導致你的網站損毀。
前一個 Service Worker 後所需的清除工作,就是刪除新 Service Worker 不需要的任何快取:
const version = 2;
const assetCacheName = `assets-${version}`;
self.addEventListener("activate", event => {
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => {
if (cacheName !== assetCacheName){
return caches.delete(cacheName);
}
});
);
});
);
});
雖然要避免 Service Worker 彼此衝突需要花費一些心力和紀律,但這一切都是值得的。
Service Worker 思維
在思考服務工作站時,請先調整正確的思維,這樣才能充滿信心地建構服務工作站。一旦掌握這些概念,您就能為使用者打造絕佳的體驗。
如果您想透過玩遊戲來瞭解這一切,那麼您就來對地方了!請前往玩Service Workies,瞭解如何使用服務工作架構來消滅離線怪獸。