Service workers 是Progressive Web Apps 的核心。它們允許快取資源和推送通知,這是原生 app 應用的兩個突出特性。
service worker 是你的網頁和網路之間的 可程式設計代理 ,它可以攔截和快取網路請求。這實際上可以讓你 使自己的 app 具有離線優先的體驗 。
Service workers 是一種特殊的 web worker:一個關聯工作環境上執行的網頁且與主執行緒分離的 JavaScript 檔案。它帶來了非阻塞這一優點 —— 所以計算處理可以在不犧牲 UI 響應的情況下完成。
因為它在單獨的執行緒上,因此它沒有訪問 DOM 的許可權,也沒有訪問本地儲存 APIs 和 XHR API 的許可權。它只能使用 Channel Messaging API 與主執行緒通訊。
Service Workers 與其他新進的 Web APIs 搭配:
PromisesFetch APICache API它們 只在使用 HTTPS 協議的頁面可用(除了本地請求不需要安全連線,這會使測試更簡單。)。
後臺執行Service workers 獨立執行,當與其相關聯的應用沒有執行的時候也可以接收訊息。
它們可以後臺執行的幾種情況:
當你的手機應用 在後臺執行 ,沒有啟用當你的手機應用 關閉 甚至沒有在後臺運行當 瀏覽器關閉 ,如果 app 執行在瀏覽器上service workers 非常有用的幾種場景:
它們可以作為 快取層 來處理網路請求,並且快取離線時要使用的內容它們允許 推送通知service worker 只有在需要的時候執行,不然則停止執行。
離線支援傳統上,web app 的離線體驗一直很差。沒有網路,web app 通常根本無法工作。另一方面,原生手機 app 則有能力提供一種可以離線執行的版本或者友好的訊息提示。
這就不是一種友好的訊息提示,但這是 Chrome 中一個網頁在沒有網路連線情況下的樣子:
最近,HTML5 AppCache 已經承諾允許 web apps 快取資源和離線工作。但是它缺乏靈活性,而且混亂的表現也讓它不足勝任這項工作(並已經停止)。
Service workers 是新的離線快取標準。
可以進行哪種快取?
在安裝期間預快取資源可以在第一次開啟 app 的時候安裝在整個應用中重用的資源,如圖片,CSS,JavaScript 檔案。
這就給出了所謂的 App Shell 體系 。
快取網路請求使用 Fetch API ,我們可以編輯來自伺服器的響應,如果伺服器無法訪問,可以從快取中提供響應作為替代。
Service Worker 生命週期service worker 經過以下三個步驟才能提供完整的功能:
註冊安裝啟用註冊註冊告訴瀏覽器 service worker 在哪裡,並在後臺開始安裝。
註冊放置在 worker.js 中 service worker 的示例程式碼:
if ('serviceWorker' in navigator) { window.addEventListener('load', () => { navigator.serviceWorker.register('/worker.js') .then((registration) => { console.log('Service Worker registration completed with scope: ', registration.scope) }, (err) => { console.log('Service Worker registration failed', err) }) })} else { console.log('Service Workers not supported') }
即使此程式碼被多次呼叫,如果 service worker 是新的,並且以前沒有被註冊,或者已更新,瀏覽器將僅執行註冊。
作用域register() 呼叫還接受一個作用域引數,該引數是一個路徑用來確定應用程式的哪一部分可以由 service worker 控制。
它預設包含 service worker 的資料夾中的所有檔案和子資料夾,所以如果將它放到根資料夾,它將控制整個 app。在子資料夾中,它將只會控制當前路徑下的頁面。
下面的示例透過指定 /notifications/ 資料夾範圍來註冊 service worker。
navigator.serviceWorker.register('/worker.js', { scope: '/notifications/' })
/ 很重要:在這種情況下,頁面 /notifications 不會觸發 service worker,而如果作用域是:
{ scope: '/notifications' }
它就會起作用。
注意:service worker 不能從一個資料夾中“提升”自己的作用域:如果它的檔案放在 /notifications 下,它不能控制 / 路徑或其他不在 /notifications 下的路徑。
安裝如果瀏覽器確定 service worker 過期或者以前從未註冊過,則會繼續安裝。
self.addEventListener('install', (event) => { //... });
這是使用 service worker 初始化快取 的好時機。然後使用 Cache API 快取 App Shell 和靜態資源。
啟用一旦 service worker 被成功註冊和安裝,第三步就是啟用。
這時,當介面載入時,service worker 就能正常工作了。
它不能和已經載入的頁面進行互動,因此 service worker 只有在使用者和應用互動的第二次或重新載入已開啟的頁面時才有用。
self.addEventListener('activate', (event) => { //... });
這個事件的一個好的用例是清除舊快取和一些關聯到舊版本並且沒有被新版本的 service worker 使用的檔案。
更新 Service Worker要更新 service worker,你只需修改其中的一個位元組。當暫存器程式碼執行的時候,它就會被更新。
一旦更新了 service worker,直到所有關聯到舊版本 service worker 已載入的頁面全部關閉,新的 service worker 才會起作用。
這確保了在已經工作的應用/頁面上不會有任何中斷。
Fetch 事件當網路請求資源時 fetch 事件 被觸發。
這給我們提供了在發起網路請求前檢視 快取 的能力。
例如,下面的程式碼片段使用 Cache API 來檢查請求的 URL 是否已經儲存在快取響應裡面。如果已存在,它會返回快取中的響應。否則,它會執行 fetch 請求並返回結果。
self.addEventListener('fetch', (event) => { event.respondWith( caches.match(event.request) .then((response) => { if (response) { //entry found in cache return response } return fetch(event.request) } ) ) })
後臺同步後臺同步允許發出的連線延遲,直到使用者有可用的網路連線。
這是確保使用者能離線使用 app,能對其進行操作,並且當網路連線時排隊進行服務端更新(而不是顯示嘗試獲取訊號的無限旋轉圈)的關鍵。
navigator.serviceWorker.ready.then((swRegistration) => { return swRegistration.sync.register('event1') });
這段程式碼監聽 service worker 中的事件:
self.addEventListener('sync', (event) => { if (event.tag == 'event1') { event.waitUntil(doSomething()) } })
doSomething() 返回一個 promise 物件。如果失敗,另一個同步事件將安排自動重試,直到成功。
這也允許應用程式在有可用網路連線時,立即從伺服器更新資料。
推送事件Service workers 讓 web apps 為使用者提供本地推送。
推送和通知實際上是兩種不同的概念和技術,它們結合起來就是我們所知的 推送通知 。推送提供了允許伺服器向 service worker 傳送訊息的機制,通知就是 servic worker 向用戶顯示資訊的方式。
因為 service workers 即使在 app 沒有執行的時候也可以執行,它們可以監聽即將到來的推送事件。然後它們要麼提供使用者通知,要麼更新 app 狀態。
推送事件用後端透過瀏覽器推送服務啟動,如Firebase 提供的推送服務。
下面這個例子展示了 web worker 如何能夠監聽到即將到來的推送事件:
self.addEventListener('push', (event) => { console.log('Received a push event', event) const options = { title: 'I got a message for you!', body: 'Here is the body of the message', icon: '/img/icon-192x192.png', tag: 'tag-for-this-notification', } event.waitUntil( self.registration.showNotification(title, options) ) })
有關控制檯日誌的說明:
如果 service work 有任何控制檯日誌語句( console.log 和其類似),請確保你打開了 Chrome Devtools(或類似工具)提供的 Preserve log 功能。
否則,由於 service worker 在頁面載入前執行,並且在載入頁面前清除了控制檯,你將不會在控制檯看到任何日誌輸出。
原文地址:Service workers: the little heroes behind Progressive Web Apps
https://medium.freecodecamp.org/service-workers-the-little-heroes-behind-progressive-web-apps-431cc22d0f16
https://medium.freecodecamp.org/@writesoftware?source=post_header_lockup
譯文出自:掘金翻譯計劃
本文永久連結:https://github.com/xitu/gold-miner/blob/master/TODO/service-workers-the-little-heroes-behind-progressive-web-apps.md
譯者:FateZeros
https://github.com/FateZeros
校對者:MechanicianW atuooo
https://github.com/MechanicianW
連結:https://juejin.cn/post/6844903569590583303