- 原文地址:Service workers: the little heroes behind Progressive Web Apps
- 原文做者:Flavio Copes
- 譯文出自:掘金翻譯計劃
- 本文永久連接:github.com/xitu/gold-m…
- 譯者:FateZeros
- 校對者:MechanicianW atuooo
Service workers 是 Progressive Web Apps 的核心。它們容許緩存資源和推送通知,這是原生 app 應用的兩個突出特性。html
service worker 是你的網頁和網絡之間的 可編程代理,它能夠攔截和緩存網絡請求。這實際上可讓你 使本身的 app 具備離線優先的體驗。前端
Service workers 是一種特殊的 web worker:一個關聯工做環境上運行的網頁且與主線程分離的 JavaScript 文件。它帶來了非阻塞這一優勢 —— 因此計算處理能夠在不犧牲 UI 響應的狀況下完成。android
由於它在單獨的線程上,所以它沒有訪問 DOM 的權限,也沒有訪問本地存儲 APIs 和 XHR API 的權限。它只能使用 Channel Messaging API 與主線程通訊。ios
Service Workers 與其餘新進的 Web APIs 搭配:git
它們 只在使用 HTTPS 協議的頁面可用(除了本地請求不須要安全鏈接,這會使測試更簡單。)。github
Service workers 獨立運行,當與其相關聯的應用沒有運行的時候也能夠接收消息。web
它們能夠後臺運行的幾種狀況:編程
service workers 很是有用的幾種場景:後端
service worker 只有在須要的時候運行,否則則中止運行。promise
傳統上,web app 的離線體驗一直不好。沒有網絡,web app 一般根本沒法工做。另外一方面,原生手機 app 則有能力提供一種能夠離線運行的版本或者友好的消息提示。
這就不是一種友好的消息提示,但這是 Chrome 中一個網頁在沒有網絡鏈接狀況下的樣子:
可能惟一的好處就是你能夠點擊恐龍來玩免費的小遊戲 —— 但這很快就會變的無聊。
最近,HTML5 AppCache 已經承諾容許 web apps 緩存資源和離線工做。可是它缺少靈活性,並且混亂的表現也讓它不足勝任這項工做(並已經中止)。
Service workers 是新的離線緩存標準。
能夠進行哪一種緩存?
能夠在第一次打開 app 的時候安裝在整個應用中重用的資源,如圖片,CSS,JavaScript 文件。
這就給出了所謂的 App Shell 體系。
使用 Fetch API,咱們能夠編輯來自服務器的響應,若是服務器沒法訪問,能夠從緩存中提供響應做爲替代。
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 纔會起做用。
這確保了在已經工做的應用/頁面上不會有任何中斷。
刷新頁面還不夠,由於舊的 worker 仍在運行,且沒有被刪除。
當網絡請求資源時 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 在頁面加載前執行,而且在加載頁面前清除了控制檯,你將不會在控制檯看到任何日誌輸出。
感謝閱讀這篇文章,關於這個主題還有不少值得學習的地方!我在關於前端開發的博客中發表了不少相關的內容,別忘記去看!😀
最初發表於flaviocopes.com。
掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。