摘要:如果返回的被拒,另一個同步事件被自動地開始重試操作,直到返回一個成功狀態(tài)的。推送機制使得服務器能夠向發(fā)送信息,然后將信息展示給用戶才是消息通知。然后它們可以發(fā)送消息通知,或者是更新的狀態(tài)。
原文地址:https://medium.freecodecamp.org/service-workers-the-little-heroes-behind-progressive-web-apps-431cc22d0f16
作者:Flavio Copes
摘要:這篇文章簡述service worker作為PWA核心技術(shù)如何實現(xiàn)資源緩存和消息推送的功能,還幫助讀者理解service worker的生命周期。
Service worker是漸進式網(wǎng)絡(luò)應用(Progressive Web Apps)的核心。它們幫助我們實現(xiàn)原本是原生app才有資源緩存和消息推送兩大特性。
Service worker是你的網(wǎng)頁與網(wǎng)絡(luò)間的代理,它能夠攔截和緩存來往的網(wǎng)絡(luò)請求。這可以幫助你的應用創(chuàng)造一個離線環(huán)境下也能良好訪問的用戶體驗。
首先介紹一下web worker的概念。它是一個與指定網(wǎng)頁相關(guān)聯(lián)的JS文件,獨立與主線程運行在一個特定的上下文環(huán)境中,這樣就不會為了計算數(shù)據(jù)去犧牲UI的性能,從而避免了阻塞的情況。而service worker是一種特殊的web worker。
而正是由于它是一個子線程,所以無法操作DOM。同樣也無法訪問Local Storage API和XHR API。它只能通過Channel Messaging API和主線程通信。
Service Worker能夠與下面幾個API合作:
Promises
Fetch API
Cache API
只有在HTTPS協(xié)議下的網(wǎng)頁里它們才會起作用。(不過不包括本地的網(wǎng)絡(luò)請求,因為它們不需要保持安全連接。這樣也方便我們調(diào)試。)
后臺進程Service worker能夠獨立于與它關(guān)聯(lián)的應用程序運行,并且在這些程序處于非活躍狀態(tài)下仍可以接收消息。
讓我來舉幾個場景:
app處于后臺非活躍狀態(tài)下運行;
app被關(guān)閉;
呈現(xiàn)你網(wǎng)頁的瀏覽器被關(guān)閉;
那么service worker將不受影響地繼續(xù)工作。
Service worker的有用之處在于:
它們可以當作緩沖層,處理網(wǎng)絡(luò)請求和緩存離線所需的資源;
它們可以用來推送消息。
Service worker只在需要的時候運行,其他情況下都會停止工作。
支持離線對于傳統(tǒng)網(wǎng)頁,離線情況下的用戶體驗非常糟糕。如果用戶沒有聯(lián)網(wǎng),移動端的web應用一般是直接停止工作。反觀原生應用,會展示給用戶一些友好的提示信息。
下面這張圖是Chrome瀏覽器中離線網(wǎng)頁顯示的內(nèi)容,顯然這并不算是一個友好的提示信息:
也許唯一不錯的地方是你可以通過點擊恐龍來免費玩一個游戲,不過相信你很快就會變得不耐煩了。
不久之前,HTML5標準下的AppCache可以讓web應用緩存離線資源,但是它缺乏靈活性并且有一些令人困惑的行為,這也說明它無法勝任支持離線這項工作。
而現(xiàn)在,service worker成了離線緩存的新標準。
那么,它實現(xiàn)哪些緩存呢?
安裝時的預緩存像圖片、CSS文件和JS文件都會在app的使用過程中重復用到。這些資源可以在app打開的第一時間緩存好。
這也是所謂的APP殼架構(gòu)( App Shell architecture)的基礎(chǔ)。
緩存網(wǎng)絡(luò)請求我們使用Fetch API可以對服務器返回的響應報文進行編輯,根據(jù)服務器是否可達來決定是否使用緩存中的響應報文代替。
生命周期一個service worker在啟動前經(jīng)歷了三步:
注冊(Registration)
安裝(Installation)
激活(Activation)
注冊注冊階段是通知瀏覽器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") }
無論這段代碼被調(diào)用多少次,瀏覽器始終只會在service worker之前沒有注冊過或是需要更新的情況下進入注冊階段。
Scoperegister函數(shù)需要一個scope參數(shù)來指明你的web應用被該service worker管理的文件所在路徑。
這個參數(shù)的默認值是所有文件以及service worker文件父級目錄下的所有子文件夾。所以如果你把service worker文件放在根目錄下,它會管理整個web應用。而如果在某個子文件夾中,它只會管理該路徑能夠訪問的網(wǎng)頁。
下面這個例子通過指定scope參數(shù)為/notifications/目錄來注冊一個service worker。
navigator.serviceWorker.register("/worker.js", { scope: "/notifications/" })
結(jié)尾的/非常重要,可以避免/notification頁面觸發(fā)service worker。而如果寫成下面這樣:
{ scope: "/notifications" }
那么service worker就將同樣作用于/notification頁面。
注意:service worker無法控制自身所在目錄以外的文件。也就是說,如果service worker文件被放在/notification文件夾下,它無法控制根目錄/或其他不屬于/notification的文件。
安裝如果瀏覽器發(fā)現(xiàn)一個service worker過期或之前沒有注冊過,那么它將安裝這個service worker。
self.addEventListener("install", (event) => { //... });
這是使用service worker初始化緩存,然后利用Cache API來緩存APP shell和靜態(tài)資源的好時機。
激活一旦service worker注冊并安裝成功后 ,我們來到了第三階段:激活。
這時,service worker能夠在加載新頁面時開始工作。
它不能作用于激活前已經(jīng)加載過的頁面,所以只有重啟app或是刷新已加載頁面兩種方式來使它工作。
self.addEventListener("activate", (event) => { //... });
監(jiān)聽這個事件可以用來清除舊緩存或者是刪除新版service worker不需要的舊資源。
更新你僅僅是修改一字節(jié)的文件就需要更新一次service worker。它會在注冊的代碼再次執(zhí)行時被更新。
更新后的service worker只有在所有頁面都被關(guān)閉后才開始代替之前的service worker工作。如果僅僅是刷新頁面是不會起作用的,因為之前的service worker仍然在運行且沒有被刪除。
這種機制保證了更新不會讓之前在運行的app或網(wǎng)頁崩潰。
Fetch事件當瀏覽器發(fā)送網(wǎng)絡(luò)請求時就會觸發(fā)Fetch事件。
我們借此可以在請求發(fā)送時檢查緩存中是否已經(jīng)存儲所需資源。
舉個例子,下面的代碼使用了Cache API來檢查請求的URL是否已經(jīng)被緩存。如果是,那么返回緩存中的響應數(shù)據(jù),否則會發(fā)送請求然后返回響應數(shù)據(jù)。
self.addEventListener("fetch", (event) => { event.respondWith( caches.match(event.request) .then((response) => { if (response) { //entry found in cache return response } return fetch(event.request) } ) ) })Background Sync
當用戶在離線狀態(tài)下發(fā)送網(wǎng)絡(luò)請求時,Background Sync這個API將延遲該請求直到用戶脫離離線狀態(tài)。
這保證了用戶在離線狀態(tài)下仍然可以使用并操作app,這些離線操作會保存在隊列中,以便在連接網(wǎng)絡(luò)后向服務端發(fā)出響應請求。(是不是比展示一個無休止的loading圖標要好多了?)
navigator.serviceWorker.ready.then((swRegistration) => { //注冊一個事件event1 return swRegistration.sync.register("event1") });
下面的代碼是在service worker中監(jiān)聽這個事件:
self.addEventListener("sync", (event) => { if (event.tag == "event1") { event.waitUntil(doSomething()) } })
doSomething()返回一個promise。如果返回的promise被拒,另一個同步事件被自動地開始重試操作,直到返回一個成功狀態(tài)的promise。
這也使得app能夠在聯(lián)網(wǎng)時立刻更新服務器發(fā)來的數(shù)據(jù)。
消息推送Service worker使得web應用可以像原生一樣推送消息給用戶。
事實上,推送和消息通知是兩個不同的概念,它們組合而成的技術(shù)才是我們熟悉的消息推送。推送機制使得服務器能夠向service worker發(fā)送信息,然后service worker將信息展示給用戶才是消息通知。
由于service worker可以在app關(guān)閉后繼續(xù)運行,所以它們能夠一直監(jiān)聽推送事件。然后它們可以發(fā)送消息通知,或者是更新app的狀態(tài)。
推送事件會在后端通過瀏覽器推送服務后啟動,比如說Firebase的推送服務。
下面的代碼演示了web worker如何監(jiān)聽push事件:
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) ) })關(guān)于console.log
如果你的代碼中包含console.log或是其他類似的控制臺輸出語句,務必打開Chrome開發(fā)工具中的Preserve log功能。
否則的話,因為service worker在網(wǎng)頁加載前就開始執(zhí)行,而此時控制臺會被清空,所以你無法看到任何日志輸出。
作者總結(jié)非常感謝閱讀本篇教程,事實上,關(guān)于PWA還有很多要學習的知識。如果您有什么見解歡迎在下面評論。
譯者注:主流瀏覽器開始逐漸支持service worker,以后PWA是否會真的與原生平分秋色呢?未來如何,我想現(xiàn)在多了解一點PWA的知識總不會壞事。
項目地址:https://github.com/WhiteYin/translation
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/92883.html
摘要:學習與實踐系列文章已整理至學習手冊,文字內(nèi)容已同步至。本文是學習與實踐系列的第三篇文章。引言其中一個令人著迷的能力就是離線可用。但是,如果你注意到文章開頭的圖片就會發(fā)現(xiàn),離線時我們不僅可以訪問,還可以使用搜索功能。 《PWA學習與實踐》系列文章已整理至gitbook - PWA學習手冊,文字內(nèi)容已同步至learning-pwa-ebook。轉(zhuǎn)載請注明作者與出處。 本文是《PWA學習與實...
摘要:小蘿卜滬江前端開發(fā)工程師本文原創(chuàng)翻譯,有不當?shù)牡胤綒g迎指出。簡稱就非常擅長做這些,事實這也是它們的宗旨。通過它精心設(shè)計的規(guī)則能保證優(yōu)先顯示頁面的主要內(nèi)容。原創(chuàng)新書移動前端高效開發(fā)實戰(zhàn)已在亞馬遜京東當當開售。 小蘿卜(滬江前端開發(fā)工程師)本文原創(chuàng)翻譯,有不當?shù)牡胤綒g迎指出。轉(zhuǎn)載請指明出處。 如果你在過去幾個月一直關(guān)注web開發(fā)社區(qū),你很可能已經(jīng)閱讀了 progressive web ap...
摘要:我喜歡移動,而且也是那些堅持使用技術(shù)構(gòu)建移動應用程序的人之一。我們準備做這樣的一個漸進式應用是典型的旨在提高用戶離線體驗的應用。當我們開始構(gòu)建應用時,你就能理解上面的場景了。的作用范圍是針對相對路徑的。最佳的做法是在應用的入口。 我喜歡移動app,而且也是那些堅持使用Web技術(shù)構(gòu)建移動應用程序的人之一。 經(jīng)過技術(shù)的不斷迭代(可能還有一些其它的東西),移動體驗設(shè)計愈來愈平易近人,給予用戶...
摘要:對來說主要兩個事件。是當前的變量,執(zhí)行該方法表示強制當前處在狀態(tài)的進入狀態(tài)。頁面關(guān)閉之后,老的會被干掉,新的接管頁面新的生效后會觸發(fā)事件。 一、傳統(tǒng)web 應用 當前web應用在移動時代并沒有達到其在桌面設(shè)備上流行的程度,下面有張圖來對比與原生應用之間的差別。 showImg(https://segmentfault.com/img/bVbaD44?w=1920&h=1080)...
閱讀 3334·2021-11-22 12:04
閱讀 2718·2019-08-29 13:49
閱讀 491·2019-08-26 13:45
閱讀 2249·2019-08-26 11:56
閱讀 1007·2019-08-26 11:43
閱讀 601·2019-08-26 10:45
閱讀 1275·2019-08-23 16:48
閱讀 2164·2019-08-23 16:07