service workers 實踐
什麼是 service workers
ofollow,noindex" target="_blank">A service worker is a type of web worker
service worker 是一個 WEB API,是 Web Workers 的一種實現,功能是可以攔截、處理請求,配合 CacheStorage 等 api,可以做到將資源儲存到本地等,實現離線訪問。
使用
"須知"
- 使用網站必須是 https 協議,本地測試的時候支援 localhost 訪問
- 不佔用執行緒、非同步設計等與 Web Workers 一樣的特性
- IE11 與 Opera Mini 不支援
- 配合 cachestorage 使用
生命週期
service workers 的生命週期有:
- 安裝 install
- 啟用 activate
載入 service workers 需要註冊(Registration),更新 service workers 則會在 install 後先進入 waiting 狀態,等到下次載入或使用skipWaiting()
進入 activate 狀態。
註冊 Registration
要使用 service workers,需要先註冊,為避免快取、瀏覽器不支援等問題,可以寫成如下格式:
if('serviceWorker' in navigator){ navigator.serviceWorker.register('serviceworker.js?V=0.0.1') }
首次進入帶有 service workers 的網站,會立即進行下載。
在舊版本瀏覽器上,如果設定的Cache-Control 的更新時間大餘 24 小時,則表現為至少 24 小時會更新一次。
在 chrome68 、firefox57 以及開始支援 service workers 的 edge、safari 的版本,會忽略Cache-Control 。
安裝 install
當頁面成功註冊了 service workers 之後,就會發生 install 事件,使用self.addEventListener
可以監聽。此時已經可以將部分資源直接儲存到 cachestroge 當中,這樣下次載入時就可以直接從 cachestorge 獲取,不必再次通過請求伺服器下載資源。考慮到符合 PWA 的漸進原則,並保證 install 成功,此處僅載入必要且少量的資源即可,如果對離線訪問有要求,可以優先儲存離線需要而線上不需要的資源。
此時需要使用到CacheStorage 。
const filesToCache = [ "/resources/js/main.js", "/resources/css/main.css", ]; //self 屬性可返回對視窗自身的只讀引用,在Workers中不能使用window物件 self.addEventListener("install", function(event) { event.waitUntil( //開啟version的cache caches.open(version).then(function(cache) { //增加需要快取的檔案 return cache.addAll(filesToCache); }) ); });
啟用 activate && 更新 update
初次 install 成功後,會直接進入 activate,如果需要更新,install 後會先保持 waiting,此時會保持使用舊版本的 js,在下一次訪問會使用新的 js 檔案。
關於 service workers 的更新:
-
每當 serviceworker.js 發生了改動,便預設更新,訪問時都會自動獲取新的 js 檔案(客戶端自身快取不考慮的情況),自然而然,給 js 加一個版本號便可實現更新。
const version = "V0.0.1";
-
如果想跳過 waiting 階段,可以在 install 之後使用
skipWaiting
方法self.addEventListener('install', function(event) { event.waitUntil(self.skipWaiting()); });
當監聽到 activate 時,可以對CacheStorage
進行更新、清理等:
self.addEventListener("activate", event => { event.waitUntil( //caches.keys返回key的list caches.keys().then(keyNameList => { //刪除舊版本快取 keyNameList.map(key => { if (key !== version) { return caches.delete(key) }; }); }) ); });
攔截與處理請求
進入到 activate 之後,我們便可以對瀏覽器傳送的請求進行攔截與處理。
在攔截請求的時候,需要注意,
cache.put
不支援"POST"方法
,可以處理的策略有使用indexedb、localstorage,或者乾脆只使"GET"請求;
self.addEventListener("fetch", event => { //Fetchevent.respondWith攔截request event.respondWith( //caches.match 返回第一個匹配的response物件 caches.match(event.request).then(resp => { return resp ||fetch(event.request).then(response => { const clonedResponse = response.clone(); // 可以使用indexeddb儲存post,也應該可以用localstorage if (response.ok && event.request.method != "POST") { caches.open(version).then(cache => { cache.put(event.request, clonedResponse); }); } return response; }); }) ); });
小結
service workers是谷歌推行的PWA 設計理念裡重要的技術組成,除了上文介紹的以外,還有很多其他API,功能還是比較強大的。
不過如果想實現離線、安裝桌面版本,還需要寫好前進、後退、導航等功能,如果流暢性不高很影響體驗。此外,PushAPI 的支援性還不是很好,通訊應該比較受限制(未具體研究)。
參考資料: