【sw】service worker概述

1. 先說說PWA盯蝴?

PWA = progressive web app = 漸進增強式web應(yīng)用 = 谷歌也想分app市場的一杯羹搞出的東西 = 用js寫本地app

由于是谷歌想搞點東西鳖敷,所以支持pwa的主要還是安卓系統(tǒng)和谷歌瀏覽器减牺。


單純依賴網(wǎng)頁的網(wǎng)站存在一些問題:

手機桌面入口不夠便捷,想要進入一個頁面必須要記住它的url或者加入書簽

沒網(wǎng)絡(luò)就沒響應(yīng),不具備離線能力

不像APP一樣能進行消息推送

基于以上幾個網(wǎng)頁應(yīng)用存在的問題,pwa橫空出世,其核心就是想要脫離瀏覽器入口環(huán)境成為可以離線運行的app妄呕,實現(xiàn)native app可以做到的事情。

那么說回來嗽测,pwa想要實現(xiàn)離線運行绪励,就必須要緩存能力與操控請求和響應(yīng)的權(quán)利,擁有安全高效的底層功能唠粥。

PWA的核心技術(shù)是ServiceWorker:

ServiceWorker給前端開發(fā)者開放了內(nèi)核大量的底層能力疏魏,比如,它給前端提供了細(xì)粒度操作請求緩存的底層原語厅贪,等同于給前端開放了操作HTTP Cache級別緩存的能力蠢护,與Fetch API結(jié)合,讓前端具備了完全操控請求养涮,響應(yīng)葵硕,緩存的能力眉抬,這點對于pwa的實現(xiàn)至關(guān)重要。因此serviceWorker是pwa的核心懈凹。

實際應(yīng)用中蜀变,為了兼容安卓與ios,serviceWorker的應(yīng)用最廣泛的還不在pwa上面介评,而是在網(wǎng)站的優(yōu)化方面库北。

ServiceWorker能在網(wǎng)頁優(yōu)化中干些啥?

Service Worker 是 Chrome 團隊提出和力推的一個 WEB API们陆,用于給 web 應(yīng)用提供高級的可持續(xù)的后臺處理能力寒瓦。

Service Worker 最主要的特點是:在頁面中注冊并安裝成功后,運行于瀏覽器后臺坪仇,不受頁面刷新的影響杂腰,可以監(jiān)聽和截攔作用域范圍內(nèi)所有頁面的 HTTP 請求

基于 Service Worker API 的特性椅文,結(jié)合 Fetch API喂很、Cache API、Push API皆刺、postMessage API 和 Notification API少辣,可以在基于瀏覽器的 web 應(yīng)用中實現(xiàn)如離線緩存、消息推送羡蛾、靜默更新等 native 應(yīng)用常見的功能漓帅,以給 web 應(yīng)用提供更好更豐富的使用體驗。

特點

圖片來源:http://lzw.me/a/pwa-service-worker.html

網(wǎng)站必須使用 HTTPS林说。除了使用本地開發(fā)環(huán)境調(diào)試時(如域名使用?localhost)??

運行于瀏覽器后臺煎殷,不受頁面刷新的影響屯伞,可以控制打開的作用域范圍下所有的頁面請求

單獨的作用域范圍腿箩,單獨的運行環(huán)境和執(zhí)行線程,可監(jiān)聽和攔截注冊Scope下的所有請求和響應(yīng)

不能操作頁面 DOM劣摇。但可以通過事件機制來處理

有獨立的與html無關(guān)的生命周期

生命周期

生命周期

參考 Service Worker 的生命周期珠移,使用 Service Worker 大概需要如下幾個過程。

install -> installed -> actvating -> Active -> Activated -> Redundant

收到事件SW線程要啟動末融,也意味著事件處理完成钧惧,SW線程是需要關(guān)閉的。SW有獨立的GlobalScope勾习,獨立的Isolate浓瞪,獨立的JS運行環(huán)境,SW線程的資源消耗是非常大的巧婶,事件驅(qū)動是減少SW線程資源消耗的一種有效的方式乾颁,這就是SW被設(shè)計成事件驅(qū)動的原因涂乌。

生命周期包含兩部分,一部分是腳本英岭,一部分是線程湾盒。

SW腳本的狀態(tài)是存儲在數(shù)據(jù)庫的,打開頁面時诅妹,會先從數(shù)據(jù)庫中讀取當(dāng)前頁面activated狀態(tài)的SW腳本罚勾,然后再派發(fā)Fetch事件去啟動SW線程。SW要控制頁面吭狡,腳本是activated狀態(tài)尖殃,線程是running狀態(tài),兩者缺一不可划煮,而這兩者的生命周期都與頁面文檔無關(guān)分衫。這就是SW文檔無關(guān)生命周期的內(nèi)在涵義。

sw腳本(sw.js) =>? ?腳本狀態(tài)存儲數(shù)據(jù)庫LevelDB開啟(activated)? =>? 派發(fā)fetch事件啟動線程? =>?

sw線程? ?=>? ?事件驅(qū)動(running)=>? sw所控制的頁面

SW腳本激活之后會存儲相關(guān)信息到LevelDB數(shù)據(jù)庫般此,再次訪問頁面時蚪战,可以直接從注冊數(shù)據(jù)庫里讀取信息,然后派發(fā)Fetch事件去啟動SW線程铐懊,SW線程啟動完成之后邀桑,所有的Fetch請求都會觸發(fā)fetch事件,前端可以監(jiān)聽fetch事件科乎,按照各種策略去獲取資源壁畸。

攔截注冊Scope下所有的請求和響應(yīng)

Scope內(nèi)的頁面,所有的請求都會經(jīng)過SW茅茂,由內(nèi)部對象負(fù)責(zé)處理捏萍。內(nèi)部對象會檢查這些資源是否在SW緩存,如果在SW緩存空闲,就會創(chuàng)建讀取緩存的任務(wù)令杈,直接從SW緩存讀取碴倾;如果不在SW緩存逗噩,就會創(chuàng)建寫入緩存的任務(wù),繼續(xù)走到網(wǎng)絡(luò)流程跌榔,并將結(jié)果寫入SW緩存异雁。如果請求不受SW控制,會直接進入網(wǎng)絡(luò)請求僧须,走正常請求的流程纲刀。

具有可靠的能力

web前端服務(wù)常被認(rèn)為是不夠可靠(Reliable)的,網(wǎng)絡(luò)會斷担平,用戶停留時間無法掌控示绊,沒有本地緩存機制〗嬲酰現(xiàn)在有了sw了,這些問題都可以解決了耻台。讀不不讀取緩存空免,更不更新scope,都在web前端的掌控中盆耽。

可以精細(xì)的控制每個資源的緩存蹋砚,讓資源更可靠;

可以用push來預(yù)加載摄杂,讓資源減輕即時的網(wǎng)絡(luò)依賴杏慰;

可以使用sync觸發(fā)后臺更新膝迎;

可以使用fetch實現(xiàn)后臺的上傳下載

sw代碼實現(xiàn)

注冊

在網(wǎng)站頁面上注冊實現(xiàn) Service Worker 功能邏輯的腳本煤禽。例如注冊?/sw/sw.js?文件键兜,參考代碼:

f('serviceWorker'innavigator) {

????navigator.serviceWorker.register('/sw/sw.js', {scope: '/'})

????????.then(registration => console.log('ServiceWorker 注冊成功!作用域為: ', registration.scope))

????????.catch(err => console.log('ServiceWorker 注冊失敗: ', err));

}

以上代碼的作用就是告訴瀏覽器映挂,你要讀我的sw.js文件哦泽篮,里面有配置喲!

Service Worker 的注冊路徑?jīng)Q定了其?scope?默認(rèn)作用范圍柑船。示例中?sw.js?是在?/sw/?路徑下帽撑,這使得該 Service Worker 默認(rèn)只會收到?/sw/?路徑下的 fetch 事件。如果存放在網(wǎng)站的根路徑下鞍时,則將會收到該網(wǎng)站的所有 fetch 事件亏拉。

如果希望改變它的作用域,可在第二個參數(shù)設(shè)置 scope 范圍逆巍。示例中將其改為了根目錄及塘,即對整個站點生效。

另外應(yīng)意識到這一點:Service Worker 沒有頁面作用域的概念锐极,作用域范圍內(nèi)的所有頁面請求都會被當(dāng)前激活的 Service Worker 所監(jiān)控笙僚。

sw.js

const CACHE_NAME = "lzwme_cache_v1.0.0";? ??// 用于標(biāo)注創(chuàng)建的緩存,也可以根據(jù)它來建立版本規(guī)范

//?列舉要默認(rèn)緩存的靜態(tài)資源溪烤,一般用于離線使用

const urlsToCache = [ '/offline.html',? '/offline.png' ];

// self 為當(dāng)前 scope 內(nèi)的上下文

self.addEventListener('install', event => {

????// event.waitUtil 用于在安裝成功之前執(zhí)行一些預(yù)裝邏輯,?但是建議只做一些輕量級和非常重要資源的緩存味咳,減少安裝失敗的概率

????// 安裝成功后 ServiceWorker 狀態(tài)會從 installing 變?yōu)?installed

????event.waitUntil (

????????caches.open(CACHE_NAME).then(cache => {? ??// 使用 cache API 打開指定的 cache 文件

????????????console.log(cache);? ? ? // 添加要緩存的資源列表

????????????return cache.addAll(urlsToCache);

????????})

????);

});

sw.js?內(nèi)監(jiān)聽了?install?事件庇勃。當(dāng)?sw.js?被安裝時會觸發(fā)?install?事件檬嘀,監(jiān)聽該事件可執(zhí)行安裝時要做的事情。示例中是緩存用于離線時使用的靜態(tài)資源责嚷,這也是最常見的行為鸳兽。

需要注意的是,只有?urlsToCache?中的文件全部安裝成功罕拂,Service Worker 才會認(rèn)為安裝完成揍异。否則會認(rèn)為安裝失敗全陨,安裝失敗則進入?redundant?(廢棄)狀態(tài)。所以這里應(yīng)當(dāng)盡量少地緩存資源(一般為離線時需要但聯(lián)網(wǎng)時不會訪問到的內(nèi)容)衷掷,以提升成功率辱姨。

安裝成功后,即進入等待(waiting)或激活(active)狀態(tài)戚嗅。在激活狀態(tài)可通過監(jiān)聽各種事件雨涛,實現(xiàn)更為復(fù)雜的邏輯需求。具體參見后文事件處理部分懦胞。

更新

如果?sw.js?文件的內(nèi)容有改動替久,當(dāng)訪問網(wǎng)站頁面時瀏覽器獲取了新的文件,它會認(rèn)為有更新躏尉,于是會安裝新的文件并觸發(fā)?install?事件蚯根。但是此時已經(jīng)處于激活狀態(tài)的舊的 Service Worker 還在運行,新的 Service Worker 完成安裝后會進入 waiting 狀態(tài)胀糜。直到所有已打開的頁面都關(guān)閉颅拦,舊的 Service Worker 自動停止,新的 Service Worker 才會在接下來打開的頁面里生效教藻。

如果希望在有了新版本時矩距,所有的頁面都得到及時更新怎么辦呢?

可以在?install?事件中執(zhí)行?skipWaiting?方法跳過 waiting 狀態(tài)怖竭,然后會直接進入?activate?階段锥债。接著在?activate?事件發(fā)生時,通過執(zhí)行?clients.claim?方法痊臭,更新所有客戶端上的 Service Worker哮肚。

// 安裝階段跳過等待,直接進入 active

self.addEventListener('install', function(event) {

????event.waitUntil(self.skipWaiting());

});

self.addEventListener('activate', event => event.waitUntil(

????Promise.all([

????????// 更新客戶端

????????clients.claim(),

????????// 清理舊版本

????????caches.keys().then(cacheList => Promise.all(

????????????cacheList.map(cacheName => {

????????????????if(cacheName !== CACHE_NAME) {

????????????????????caches.delete(cacheName);

????????????????}

????????????})

????????))

????])

))

另外要注意一點广匙,sw.js?文件可能會因為瀏覽器緩存問題允趟,當(dāng)文件有了變化時,瀏覽器里還是舊的文件鸦致。這會導(dǎo)致更新得不到響應(yīng)潮剪。如遇到該問題,可嘗試這么做:在 webserver 上添加對該文件的過濾規(guī)則分唾,不緩存或設(shè)置較短的有效期抗碰。注意,不要想著不同版本使用不同的文件名稱绽乔,這會帶來混亂的問題弧蝇。



參考文章:

https://zhuanlan.zhihu.com/p/50439549

http://lzw.me/a/pwa-service-worker.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子看疗,更是在濱河造成了極大的恐慌沙峻,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件两芳,死亡現(xiàn)場離奇詭異摔寨,居然都是意外死亡,警方通過查閱死者的電腦和手機怖辆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門祷肯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人疗隶,你說我怎么就攤上這事佑笋。” “怎么了斑鼻?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵蒋纬,是天一觀的道長。 經(jīng)常有香客問我坚弱,道長蜀备,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任荒叶,我火速辦了婚禮碾阁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘些楣。我一直安慰自己脂凶,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布愁茁。 她就那樣靜靜地躺著蚕钦,像睡著了一般。 火紅的嫁衣襯著肌膚如雪鹅很。 梳的紋絲不亂的頭發(fā)上嘶居,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機與錄音促煮,去河邊找鬼邮屁。 笑死,一個胖子當(dāng)著我的面吹牛菠齿,可吹牛的內(nèi)容都是我干的佑吝。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼泞当,長吁一口氣:“原來是場噩夢啊……” “哼迹蛤!你這毒婦竟也來了民珍?” 一聲冷哼從身側(cè)響起襟士,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤盗飒,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后陋桂,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體逆趣,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年嗜历,在試婚紗的時候發(fā)現(xiàn)自己被綠了宣渗。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡梨州,死狀恐怖痕囱,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情暴匠,我是刑警寧澤鞍恢,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站每窖,受9級特大地震影響帮掉,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜窒典,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一蟆炊、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧瀑志,春花似錦涩搓、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至岸霹,卻和暖如春疾层,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背贡避。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工痛黎, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人刮吧。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓湖饱,卻偏偏與公主長得像,于是被迫代替她去往敵國和親杀捻。 傳聞我的和親對象是個殘疾皇子井厌,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,979評論 2 355