摘要:生命周期的生命周期和網(wǎng)頁完全不相關(guān)。意即會(huì)作用于整個(gè)源地址上。激活安裝完之后下一步即激活。同時(shí)檢查響應(yīng)類型是否為,即檢查請求是否同域。創(chuàng)建新的的過程將會(huì)啟動(dòng),然后觸發(fā)事件??梢岳媒俪志W(wǎng)絡(luò)連接和偽造響應(yīng)數(shù)據(jù)。
原文請查閱這里,略有刪減,本文采用知識共享署名 4.0 國際許可協(xié)議共享,BY Troland。
本系列持續(xù)更新中,Github 地址請查閱這里。
這是 JavaScript 工作原理的第八章。
或許你已經(jīng)了解到漸進(jìn)式網(wǎng)絡(luò)應(yīng)用將只會(huì)越來越流行,因?yàn)樗荚趧?chuàng)造擁有更加流暢的用戶體驗(yàn)的網(wǎng)絡(luò)應(yīng)用和創(chuàng)建類 App 的原生應(yīng)用體驗(yàn)而非瀏覽器端那樣的外觀和體驗(yàn)。
構(gòu)建漸進(jìn)式網(wǎng)絡(luò)應(yīng)用的主要需求之一即在各種網(wǎng)絡(luò)和數(shù)據(jù)加載的條件下仍然可用-它可以在網(wǎng)絡(luò)不穩(wěn)定或者沒有網(wǎng)絡(luò)的情況下使用。
本文我們將會(huì)深入了解 Service Workers:它們是如何工作的以及你所應(yīng)該關(guān)切的方面。最后,我們將會(huì)列出一些Service Workers 可供利用的,獨(dú)有的優(yōu)勢并且分享我們在 SessionStack 中的團(tuán)隊(duì)實(shí)踐經(jīng)驗(yàn)。
概述若想理解 Service Workers 相關(guān)的一切,你首先應(yīng)該閱讀一下之前發(fā)布的有關(guān) Web Workers 的文章。
大體上,Service Worker 是一種 Web Worker,更準(zhǔn)確地說,它更像是一個(gè) Shared Worker。
Service Worker 運(yùn)行在其全局腳本上下文中
不指定和某個(gè)網(wǎng)頁綁定
不能夠訪問 DOM
Service Worker 接口之所以讓人感到興奮的原因之一即它支持網(wǎng)絡(luò)應(yīng)用離線運(yùn)行,這使得開發(fā)者能夠完全控制網(wǎng)絡(luò)應(yīng)用的行為。
生命周期Service Worker 的生命周期和網(wǎng)頁完全不相關(guān)。它由以下幾個(gè)步驟組成:
下載
安裝
激活
下載這發(fā)生于瀏覽器下載包含 Service Worker 相關(guān)代碼的 .js 文件。
安裝為了在網(wǎng)絡(luò)應(yīng)用中使用 Service Worker,首先得在 JavaScript 代碼中對其進(jìn)行注冊。當(dāng) Service Worker 注冊的時(shí)候,它會(huì)讓瀏覽器在后臺開始安裝 Service Worker 的步驟。
通過注冊 Service Worker,瀏覽器知曉包含 Service Worker 相關(guān)代碼的 JavaScript 文件??聪氯缦麓a:
if ("serviceWorker" in navigator) { window.addEventListener("load", function() { navigator.serviceWorker.register("/sw.js").then(function(registration) { // 注冊成功 console.log("ServiceWorker registration successful"); }, function(err) { // 注冊失敗 console.log("ServiceWorker registration failed: ", err); }); }); }
以上代碼首先檢查當(dāng)前執(zhí)行環(huán)境是否支持 Service Worker API。如果是,則注冊 /sw.js Service Worker。
可以在每次頁面加載的時(shí)候,任意調(diào)用 register()-瀏覽器會(huì)檢測 service worker 是否已經(jīng)注冊從而進(jìn)行適當(dāng)?shù)靥幚怼?/p>
register() 方法里面需要特別注意的地方即 Service Worker 文件地址。當(dāng)前示例是在服務(wù)器根目錄下。意即 service worker 會(huì)作用于整個(gè)源地址上。換句話說,即 service worker 會(huì)接收到該域名下所有頁面 的 fetch 事件。如果注冊 service worker 的文件路徑是 /example/sw.js ,那么 service worker 會(huì)接收到所有頁面路徑以 /example/ 為開頭的 URL 地址的 fetch 事件(比如 /example/page1/ /example/page2/)。
在安裝階段,最好加載和緩存一些靜態(tài)資源。一旦靜態(tài)資源緩存成功,Service Worker 的安裝也就完成了。倘若加載失?。璖ervice Worker 將會(huì)重試。一旦安裝成功,靜態(tài)資源也就緩存成功了。
這也就回答了為什么要在 load 事件之后注冊 Service Worker。這不是必須的,但是強(qiáng)烈推薦這么做。
為什么要這樣做呢?假設(shè)用戶第一次訪問網(wǎng)絡(luò)應(yīng)用?,F(xiàn)在還沒有注冊 service worker,而且瀏覽器無法事先知曉是否會(huì)最終安裝它。如果進(jìn)行安裝,則瀏覽器將會(huì)為增加的線程開辟額外的 CPU 和內(nèi)存,而這些資源原本是用來渲染網(wǎng)頁的。
參考下這里,load 事件會(huì)加載完所有的資源比如圖片,樣式之后觸發(fā)。
最終的結(jié)果即是如果在頁面中安裝 Service Worker,將有可能導(dǎo)致頁面延遲加載和渲染-不能夠讓用戶盡快地訪問網(wǎng)頁。
需要注意的是這只會(huì)發(fā)生在第一次訪問頁面的時(shí)候。后續(xù)的頁面訪問不會(huì)被 Service Worker 的安裝所影響。一旦在首次訪問頁面的時(shí)候激活了 Service Worker?,它就可以處理后續(xù)的頁面訪問所觸發(fā)的頁面加載/緩存事件。這是正確的,Service Worker 需要加載好以處理有限的網(wǎng)絡(luò)帶寬。
激活安裝完之后下一步即激活。該步驟是操作之前緩存資源的絕佳時(shí)機(jī)。
一旦激活,Service Worker 就可以開始控制在其作用域內(nèi)的所有頁面。一個(gè)有趣的事實(shí)即:注冊了 Service Worker 的頁面直到再次加載的時(shí)候才會(huì)被 Service Worker 進(jìn)行處理。當(dāng) Service Worker 開始進(jìn)行控制,它有以下幾種狀態(tài):
處理來自頁面的網(wǎng)絡(luò)或者消息請求所觸發(fā)的 fetch 及 message 事件
中止以節(jié)約內(nèi)存
以下即其生命周期:
處理 Service Worker 內(nèi)部的安裝過程在頁面運(yùn)行注冊 Service Worker 的過程中,讓我們來看看 Service Worker 腳本中發(fā)生的事情,它監(jiān)聽 Service Worker 實(shí)例的 install 事件。
以下為處理 install 事件所需要執(zhí)行的步驟:
打開緩存
緩存文件
確認(rèn)是否所有的靜態(tài)資源已緩存
以下為一個(gè) Service Worker 內(nèi)部可能的簡單安裝代碼:
var CACHE_NAME = "my-web-app-cache"; var urlsToCache = [ "/", "/styles/main.css", "/scripts/app.js", "/scripts/lib.js" ]; self.addEventListener("install", function(event) { // event.waitUntil 使用 promise 來獲得安裝時(shí)長及安裝是否失敗 event.waitUntil( caches.open(CACHE_NAME) .then(function(cache) { console.log("Opened cache"); return cache.addAll(urlsToCache); }) ); });
如果文件都成功緩存,則 service worker 安裝成功。如果任意文件下載失敗,那么 service worker 將會(huì)安裝失敗。因此,請注意需要緩存的文件。
處理 install 事件完全是可選,當(dāng)不進(jìn)行處理的時(shí)候,跳過以上幾個(gè)步驟即可。
緩存運(yùn)行時(shí)請求該部分才是干貨。在這里可以看到如何攔截請求然后返回已創(chuàng)建的緩存(以及創(chuàng)建新的緩存)。
當(dāng) Service Worker 安裝完成之后,用戶會(huì)導(dǎo)航到另一個(gè)頁面或者刷新當(dāng)前頁面,Service Worker 將會(huì)收到 fetch 事件。這里有一個(gè)演示了如何返回緩存的靜態(tài)資源或執(zhí)行一個(gè)新的請求并緩存返回結(jié)果的過程的示例:
self.addEventListener("fetch", function(event) { event.respondWith( // 該方法查詢請求然后返回 Service Worker 創(chuàng)建的任何緩存數(shù)據(jù)。 caches.match(event.request) .then(function(response) { // 若有緩存,則返回 if (response) { return response; } // 復(fù)制請求。請求是一個(gè)流且只能被使用一次。因?yàn)橹耙呀?jīng)通過緩存使用過一次了,所以為了在瀏覽器中使用 fetch,需要復(fù)制下該請求。 var fetchRequest = event.request.clone(); // 沒有找到緩存。所以我們需要執(zhí)行 fetch 以發(fā)起請求并返回請求數(shù)據(jù)。 return fetch(fetchRequest).then( function(response) { // 檢測返回?cái)?shù)據(jù)是否有效 if(!response || response.status !== 200 || response.type !== "basic") { return response; } // 復(fù)制返回?cái)?shù)據(jù),因?yàn)樗彩橇鳌R驗(yàn)槲覀兿胍獮g覽器和緩存一樣使用返回?cái)?shù)據(jù),所以必須復(fù)制它。這樣就有兩個(gè)流 var responseToCache = response.clone(); caches.open(CACHE_NAME) .then(function(cache) { // 把請求添加到緩存中以備之后的查詢用 cache.put(event.request, responseToCache); }); return response; } ); }) ); });
大概的流程如下:
event.respondWith() 會(huì)決定如何響應(yīng) fetch 事件。 caches.match() 查詢請求然后返回之前創(chuàng)建的緩存中的任意緩存數(shù)據(jù)并返回 promise。
如果有,則返回該緩存數(shù)據(jù)。
否則,執(zhí)行 fetch 。
檢查返回的狀態(tài)碼是否是 200。同時(shí)檢查響應(yīng)類型是否為 basic,即檢查請求是否同域。當(dāng)前場景不緩存第三方資源的請求。
把返回?cái)?shù)據(jù)添加到緩存中。
因?yàn)檎埱蠛晚憫?yīng)都是流而流數(shù)據(jù)只能被使用一次,所以必須進(jìn)行復(fù)制。而且由于緩存和瀏覽器都需要使用它們,所以必須進(jìn)行復(fù)制。
更新 Service Worker當(dāng)用戶訪問網(wǎng)絡(luò)應(yīng)用的時(shí)候,瀏覽器會(huì)在后臺試圖重新下載包含 Service Worker 代碼的 .js 文件。
如果下載下來的文件和當(dāng)前的 Service Worker 代碼文件有一丁點(diǎn)兒不同,瀏覽器會(huì)認(rèn)為文件發(fā)生了改變并且會(huì)創(chuàng)建一個(gè)新的 Service Worker。
創(chuàng)建新的 Service Worker 的過程將會(huì)啟動(dòng),然后觸發(fā) install 事件。然而,這時(shí)候,舊的 Service Worker 仍然控制著網(wǎng)絡(luò)應(yīng)用的頁面意即新的 Service Worker 將會(huì)處于 waiting 狀態(tài)。
一旦關(guān)閉網(wǎng)絡(luò)應(yīng)用當(dāng)前打開的頁面,舊的 Service Worker 將會(huì)被瀏覽器殺死而新的 Service Worker 就可以上位了。這時(shí)候?qū)?huì)觸發(fā) activate 事件。
為什么所有這一切是必須的呢?這是為了避免在不同選項(xiàng)卡中同時(shí)運(yùn)行不同版本的的網(wǎng)絡(luò)應(yīng)用所造成的問題-一些在網(wǎng)頁中實(shí)際存在的問題且有可能會(huì)產(chǎn)生新的 bug(比如當(dāng)在瀏覽器中本地存儲(chǔ)數(shù)據(jù)的時(shí)候卻擁有不同的數(shù)據(jù)庫結(jié)構(gòu))。
從緩存中刪除數(shù)據(jù)activate 回調(diào)中最為常見的步驟即緩存管理。因?yàn)槿粝雱h除安裝步驟中老舊的緩存,而這又會(huì)導(dǎo)致 Service Workers 無法獲取該緩存中的文件數(shù)據(jù),所以,這時(shí)候需要進(jìn)行緩存管理。
這里有一個(gè)示例演示如何把未在白名單中的緩存刪除(該情況下,以 page-1 或者 page-2 來進(jìn)行命名):
self.addEventListener("activate", function(event) { var cacheWhitelist = ["page-1", "page-2"]; event.waitUntil( // 獲得緩存中所有鍵 caches.keys().then(function(cacheNames) { return Promise.all( // 遍歷所有的緩存文件 cacheNames.map(function(cacheName) { // 若緩存文件不在白名單中,刪除之 if (cacheWhitelist.indexOf(cacheName) === -1) { return caches.delete(cacheName); } }) ); }) ); });HTTPS 要求
當(dāng)處于開發(fā)階段的時(shí)候,可以通過 localhost 來使用 Service Workers?,但當(dāng)處于發(fā)布環(huán)境的時(shí)候,必須部署好 HTTPS(這也是使用 HTTPS 的最后一個(gè)原因了)。
可以利用 Service Worker劫持網(wǎng)絡(luò)連接和偽造響應(yīng)數(shù)據(jù)。如果不使用 HTTPS,網(wǎng)絡(luò)應(yīng)用會(huì)容易遭受中間人 攻擊。
為了保證安全,必須通過 HTTPS 在頁面上注冊 Service Workers,這樣就可以保證瀏覽器接收到的 Service Worker 沒有在傳輸過程中被篡改。
瀏覽器支持Service Workers 擁有良好的瀏覽器兼容性。
你可以追蹤所有瀏覽器的支持進(jìn)程:
https://jakearchibald.github....
Service Workers 提供了更多的功能的可能Service Worker 獨(dú)有的功能:
推送通知-允許用戶選擇定時(shí)接收網(wǎng)絡(luò)應(yīng)用的推送更新
后臺同步-允許延遲操作直到網(wǎng)絡(luò)連接穩(wěn)定之后。這樣就可以保證用戶即時(shí)發(fā)送數(shù)據(jù)。
定期同步(以后支持)-提供了管理進(jìn)行定期后臺數(shù)據(jù)同步的功能
Geofencing (以后支持)-可以自定義參數(shù),也即 geofences ,該參數(shù)包含著用戶所感興趣的地區(qū)。當(dāng)設(shè)備穿過某片地理圍欄的時(shí)候會(huì)收到通知,這就能夠讓你基于用戶的地理位置來提供有用的用戶體驗(yàn)。
這里提到的每個(gè)功能將會(huì)該系列之后的文章中進(jìn)行詳細(xì)闡述。
我們持續(xù)不斷地工作以讓 SessionStack 的交互體驗(yàn)盡可能流暢,優(yōu)化頁面加載時(shí)間和響應(yīng)時(shí)間。
當(dāng)在 SessionStack 上重放用戶會(huì)話或者實(shí)時(shí)流播放,SessionStack 界面會(huì)從服務(wù)器持續(xù)抓取數(shù)據(jù)從而為用戶創(chuàng)造一個(gè)類緩沖的使用體驗(yàn)(類似視頻緩沖那種)。再詳細(xì)了解一些原理即一旦在網(wǎng)絡(luò)應(yīng)用中集成 SessionStack 庫,它將會(huì)持續(xù)收集諸如 DOM 變化,用戶交互,網(wǎng)絡(luò)請求,未處理異常以及調(diào)試信息的數(shù)據(jù)。
當(dāng)重放或者實(shí)時(shí)觀看一個(gè)會(huì)話的時(shí)候,SessionStack 會(huì)返回所有數(shù)據(jù)以方便觀察發(fā)生于用戶瀏覽器的所有事件。(視覺上和技術(shù)上的)。所有的這一切都是即時(shí)發(fā)生的,因?yàn)槲覀儾幌胱層脩舻却?/p>
由于數(shù)據(jù)是由前端抓取的,這個(gè)時(shí)候就可以使用 Service?Workers 來處理類似播放器重載和再次流傳輸所有數(shù)據(jù)的情形。處理緩慢的網(wǎng)絡(luò)連接也是非常重要的。
參見維基百科關(guān)于流的定義,可以更好地理解這里的流的概念。
本系列持續(xù)更新中,Github 地址請查閱這里。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/95310.html
摘要:的生命周期的生命周期與頁面完全分離。換句話說,這個(gè)將為這個(gè)域中的所有內(nèi)容接收事件。這不是必要的,但絕對是推薦的。新的將啟動(dòng)并且安裝事件將被移除。使用,可以很容易被劫持連接并偽造響應(yīng)。后臺同步允許延遲操作,直到用戶具有穩(wěn)定的連接。 這是專門探索 JavaScript 及其所構(gòu)建的組件的系列文章的第8篇。 想閱讀更多優(yōu)質(zhì)文章請猛戳GitHub博客,一年百來篇優(yōu)質(zhì)文章等著你! 如果你錯(cuò)過了前...
摘要:的生命周期生命周期與您的網(wǎng)頁是完全分開。激活安裝之后,下一步是將其激活。一旦激活,將開始控制所有屬于其范圍的頁面。否則,將執(zhí)行事件。響應(yīng)結(jié)果被添加到緩存中。請求和響應(yīng)必須被克隆,因?yàn)樗鼈兪橇?。新的將啟?dòng)并且事件將被觸發(fā)。 showImg(https://segmentfault.com/img/bV9Sa2?w=800&h=410);您可能已經(jīng)知道,漸進(jìn)式Web應(yīng)用(PWA)會(huì)越來越受...
摘要:為了方便大家共同學(xué)習(xí),整理了之前博客系列的文章,目前已整理是如何工作這個(gè)系列,可以請猛戳博客查看。以下列出該系列目錄,歡迎點(diǎn)個(gè)星星,我將更友動(dòng)力整理理優(yōu)質(zhì)的文章,一起學(xué)習(xí)。 為了方便大家共同學(xué)習(xí),整理了之前博客系列的文章,目前已整理 JavaScript 是如何工作這個(gè)系列,可以請猛戳GitHub博客查看。 以下列出該系列目錄,歡迎點(diǎn)個(gè)星星,我將更友動(dòng)力整理理優(yōu)質(zhì)的文章,一起學(xué)習(xí)。 J...
摘要:字節(jié)流這個(gè)簡單的模型將數(shù)據(jù)存儲(chǔ)為長度不透明的字節(jié)字符串變量,將任何形式的內(nèi)部組織留給應(yīng)用層。字節(jié)流數(shù)據(jù)存儲(chǔ)的代表例子包括文件系統(tǒng)和云存儲(chǔ)服務(wù)。使用同步存儲(chǔ)會(huì)阻塞主線程,并為應(yīng)用程序的創(chuàng)建凍結(jié)體驗(yàn)。 這是專門探索 JavaScript 及其所構(gòu)建的組件的系列文章的第 16 篇。 想閱讀更多優(yōu)質(zhì)文章請猛戳GitHub博客,一年百來篇優(yōu)質(zhì)文章等著你! 如果你錯(cuò)過了前面的章節(jié),可以在這里找到它...
閱讀 819·2023-04-25 19:28
閱讀 1460·2021-09-10 10:51
閱讀 2469·2019-08-30 15:55
閱讀 3462·2019-08-26 13:55
閱讀 3068·2019-08-26 13:24
閱讀 3383·2019-08-26 11:46
閱讀 2807·2019-08-23 17:10
閱讀 1483·2019-08-23 16:57