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