摘要:誕生之初,是單線程的。當(dāng)接收到服務(wù)端的響應(yīng)之后,便通過回調(diào)函數(shù)執(zhí)行之后的操作。沖鋒基于事件驅(qū)動。擁有攔截請求消息推送靜默更新地理圍欄等服務(wù)。控制時處于兩種狀態(tài)之一終止以節(jié)省內(nèi)存監(jiān)聽獲取和消息事件。支持的所有事件五銷毀瀏覽器決定是否銷毀。
這次體驗一種新的博客風(fēng)格,我們長話短說,針針見“血”。
備馬在深入 Service Worker 之前,我們需要快速回顧如下基礎(chǔ)。
誕生之初,JavaScript 是單線程的。
進(jìn)程有私有的虛擬地址空間、代碼、數(shù)據(jù)和其它系統(tǒng)資源,進(jìn)程申請創(chuàng)建和使用的系統(tǒng)資源會隨其終止而銷毀。線程運行在進(jìn)程之中,系統(tǒng)創(chuàng)建進(jìn)程之后就開始啟動執(zhí)行進(jìn)程的主線程,并隨主線程的退出而終止。
JavaScript 作為瀏覽器腳本語言,為方便準(zhǔn)確無誤的操作 DOM,誕生之初便采用了單線程的方式。舉個例子,若多線程同時分別刪除和修改同一個 DOM,我們很難預(yù)知其執(zhí)行結(jié)果。
但單線程中,必須通過異步和回調(diào)來優(yōu)化耗時操作。
我們在網(wǎng)頁上提交一個表單,并不希望在提交后頁面卡頓,一直等待服務(wù)端返回的提交結(jié)果。這時我們需要能在單線程中發(fā)送異步請求,點擊提交表單后可以先在頁面進(jìn)行其他操作。
Ajax 讓我們可以向后端發(fā)送異步請求,同時不影響用戶在界面中繼續(xù)操作。當(dāng) Ajax 接收到服務(wù)端的響應(yīng)之后,便通過回調(diào)函數(shù)執(zhí)行之后的操作。一個典型的異步 Ajax 實戰(zhàn)場景如下:
// 生成可發(fā)送同步/異步請求的 XMLHttpRequest 對象實例 var oReq = new XMLHttpRequest(); // open 方法初始化請求方法、地址,第三個參數(shù) true 聲明進(jìn)行異步請求 oReq.open("GET", "http://www.jianshu.com/", true); // 請求的整個過程中有五種狀態(tài),且同一時刻只能存在一種狀態(tài): // 1. 未打開 // 2. 未發(fā)送 // 3. 已獲取響應(yīng)體 // 4. 正在下載響應(yīng)體 // 5. 請求完成 // 當(dāng)請求狀態(tài)發(fā)生改變時,觸發(fā) onreadystatechange 會被調(diào)用 oReq.onreadystatechange = function (oEvent) { // 如果已經(jīng)開始下載響應(yīng)體了 if (oReq.readyState === 4) { // 如果響應(yīng)體成功下載,并且服務(wù)端返回 200 狀態(tài)碼 if (oReq.status === 200) { // 打印響應(yīng)信息 console.log(oReq.responseText); } else { console.log("Error", oReq.statusText); } } }; // send 方法發(fā)送請求,由于此請求是異步的,該方法立刻返回 oReq.send(null);
當(dāng)我們的多個請求需要依賴于上一個請求的服務(wù)端響應(yīng)時,回調(diào)函數(shù)中 Ajax 的層級逐步提高,可維護(hù)性極度下降,這就是回調(diào)地獄。
I Promise U that I`ll Marry U!!!
Promise 由 ES6 標(biāo)準(zhǔn)原生支持。正如題名,Promise 作出諾言,也要因此承擔(dān)成功(fulfilled)或失敗(rejected)的結(jié)果,以便解決回調(diào)地獄問題:
// 生成一個 Promise 實例,傳入有特定的兩個參數(shù)的匿名函數(shù) // Promise 初始狀態(tài)是 pending // resolve 被調(diào)用時,將 Promise 狀態(tài)改為成功(fulfilled) // reject 被調(diào)用時,將 Promise 狀態(tài)改為失敗(rejected) // 該匿名函數(shù)拋出錯誤時,Promise 狀態(tài)為失敗(rejected) var a = new Promise(function(resolve, reject) { // setTimeout() 模擬異步請求,成功后執(zhí)行 resolve() 方法 setTimeout(function() { resolve("1") }, 2000) }) a.then(function(val){ // then() 有兩個函數(shù)作為參數(shù),onfulfilled 和 onrejected // 當(dāng) Promise 狀態(tài)為 fulfilled 時,調(diào)用 then 的 onfulfilled 方法 // 當(dāng) Promise 狀態(tài)為 rejected 時,調(diào)用 then 的 onrejected 方法 console.log(val) // then() 方法返回 Promise 對象實例,所以可被鏈?zhǔn)秸{(diào)用 return new Promise(function(resolve, reject) { setTimeout(function() { resolve("2") }, 2000) }) }) .then(function(val) { // 鏈?zhǔn)秸{(diào)用的第二個環(huán)節(jié),處理上一個環(huán)節(jié)返回的 Promise 對象 console.log(val) })
Promise 對象的生命周期如下圖。
除了異步編程,我們還可以有 Web Worker。
通過異步編程,我們的頁面可以邊響應(yīng)用戶的下一步操作邊等待服務(wù)端的回應(yīng),不再擁有阻塞感,但 JavaScript 的單線程問題并沒有得到相應(yīng)的解決。通過 HTML 5 標(biāo)準(zhǔn)支持的 Web Worker,我們可以為 JavaScript 創(chuàng)建運行在后臺的額外線程,并被多個頁面共享。
在一個簡單的 Web Worker 實例中,main.js 和 task.js 的源碼如下。
// main.js // 實例化 Worker 對象,其實質(zhì)為新創(chuàng)建的工作線程在主線程的引用 var worker = new Worker("task.js") // postMessage 方法與新創(chuàng)建的工作線程通信 worker.postMessage({ id:1, msg:"Hello World" }); // 當(dāng) Worker 線程返回數(shù)據(jù)時,onmessage 回調(diào)函數(shù)執(zhí)行 worker.onmessage = function(message) { var data = message.data; console.log(JSON.stringify(data)) // terminate 方法終止 worker 線程的運行 worker.terminate() }; // 當(dāng) Worker 線程出錯時,onerror 回調(diào)函數(shù)執(zhí)行 // error 參數(shù)中封裝了錯誤對象的文件名、出錯行號和具體錯誤信息 worker.onerror = function(error) { console.log(error.filename, error.lineno, error.message) }
// task.js onmessage = function(message) { var data = message.data data.msg = "Hi from task.js" postMessage(data) }
在 Chrome 瀏覽器里,以上代碼必須運行在 Web 容器如 Apache 中。同時,WebKit 內(nèi)核加載并執(zhí)行 Worker 線程的流程如下圖所示。
上述知識點的詳盡博客盡請期待,您可以先查閱其它資料進(jìn)行補充。沖鋒
Service Worker 基于 Web Worker 事件驅(qū)動。
Service Worker 同樣可以在瀏覽器后臺掛起新線程,來緩解 JavaScript 的單線程問題。并且,我們可以用 Service Worker 攔截網(wǎng)絡(luò)請求進(jìn)行本地緩存或請求轉(zhuǎn)發(fā),相當(dāng)于充當(dāng)服務(wù)端與瀏覽器、瀏覽器與 Web 應(yīng)用程序之間的代理服務(wù)器。
Service Worker 帶來了速度,極大的提高了用戶體驗。
Service Worker 可有效加快重復(fù)訪問網(wǎng)絡(luò)應(yīng)用的速度。
擁有攔截請求、消息推送、靜默更新、地理圍欄等服務(wù)。
可以在客戶端通過 indexedDB API 保存持久化信息。
Service Worker 大量使用 Promise 對象。
因為通常 Service Worker 會等待響應(yīng)后繼續(xù),并根據(jù)響應(yīng)返回一個成功或者失敗的操作。Promise 非常適合這種場景。
零、Service Worker 的生命周期。
所謂生命周期,包括 Service Worker 的注冊、安裝、激活、控制和銷毀時的全部過程。我們需要對 Service Worker 的生命周期有所了解。
先決條件:
瀏覽器支持:Service Worker。
在 localhost 域或 HTTPS 域下運行:介于我們能夠通過使用 Service Worker 劫持連接、編撰以及過濾響應(yīng)來進(jìn)行權(quán)限較高的操作。
注冊:注冊過程獨立于網(wǎng)頁,先在頁面執(zhí)行注冊,之后在瀏覽器后臺啟動安裝步驟。
安裝:通常需要緩存某些靜態(tài)資源。當(dāng)所有文件已成功緩存,則安裝完畢。如果任何文件下載失敗或緩存失敗,則安裝失敗,無法激活。
激活:管理就緩存的絕佳機會。激活后它將會對作用域頁面實時控制,不過首次注冊該服務(wù)工作線程的頁面需要再次加載才會受其控制。
控制時:處于兩種狀態(tài)之一:
①、終止以節(jié)省內(nèi)存;
②、監(jiān)聽獲取 fetch 和消息 message 事件。
銷毀:由瀏覽器決定,因此盡量不要留存全局變量。
一、注冊 Service Worker。
當(dāng)瀏覽器對 Service Worker 提供原生支持時,我們便可以在頁面加載后注冊指定的 JavaScript 文件,并運行在后臺線程之中,以下代碼是這一過程的實例。
ServiceWorker Hello World!
這里通過 php 內(nèi)置命令監(jiān)聽項目目錄,便能看到 Service Worker 注冊成功。同時,在 Chrome 瀏覽器里,可以訪問 chrome://inspect/#service-workers和 chrome://serviceworker-internals/ 來檢查 Service Worker 是否已經(jīng)啟用。
二、安裝 Service Worker。
安裝階段,我們可以執(zhí)行任何任務(wù)。這里我們逐步打開緩存、緩存文件和確認(rèn)所有需要的資產(chǎn)是否緩存。ServiceWorker.js 中的實例安裝代碼如下:
var CACHE_NAME = "my-site-cache-v1"; var urlsToCache = [ "/", "/styles/main.css", "/script/main.js" ]; self.addEventListener("install", function(event) { // Perform install steps event.waitUntil( caches.open(CACHE_NAME) .then(function(cache) { console.log("Opened cache"); return cache.addAll(urlsToCache); }) ); });
這要求我們在與項目根目錄下建立 main.js 和 main.css 空文件。我們可以在 Chrome 開發(fā)者工具里的“Application”菜單的“Cache Storage”中看到相應(yīng)的緩存。并且在圖中的“Service Workers”選項卡中看到正在運行的 Service Workers。
且從上面的代碼可以看到,通過 Service Worker 對象加載的文件擁有全局變量 caches 等,并且 self 關(guān)鍵字指向這個對象本身。cache 使我們可以存儲網(wǎng)絡(luò)響應(yīng)發(fā)來的資源,并且根據(jù)它們的請求來生成 key。這個 API 和瀏覽器的標(biāo)準(zhǔn)的緩存工作原理很相似,且會持久存在,直到我們釋放主動空間——我們擁有全部的控制權(quán)。
三、激活 Service Worker。
當(dāng) Service Worker 安裝成功后,便被激活,這時可實時控制作用域中的所有網(wǎng)站,進(jìn)行緩存文件等操作。不過首次使用 Service Worker 的頁面需要再次加載才會受其控制。
四、控制 Service Worker
以下列舉幾個常見的 Service Worker 應(yīng)用場景。
1. 文件緩存
self.addEventListener("fetch", function(event) { event.respondWith( // 以下方法檢視請求,并從服務(wù)工作線程所創(chuàng)建的任何緩存中查找緩存的結(jié)果。 caches.match(event.request) .then(function(response) { console.log(event.request) console.log(caches) // 如果發(fā)現(xiàn)匹配的響應(yīng),則返回緩存的值 if (response) { return response; } return fetch(event.request); } ) ); });
通過上述文件緩存過程,我們可以告訴 Service Worker 如何使用這些緩存文件,并通過 fetch 事件來捕獲。fetch 事件只會在瀏覽器準(zhǔn)備請求 Service Worker 控制的資源時才會被觸發(fā)。這些資源包括了指定的 scope 內(nèi)的文檔,和這些文檔內(nèi)引用的其他任何資源。
2. 多頁面?zhèn)鬟f消息
我們可以打開多個 https://nzv3tos3n.qnssl.com/m... 測試頁面來進(jìn)行測試,效果如下。
其中,index.js 源碼為:
(function () { if (navigator.serviceWorker) { // 獲取頁面 DOM 元素 var msgIpt = document.getElementById("ipt"), showArea = document.getElementById("show"), sendBtn = document.getElementById("sendBtn"); navigator.serviceWorker.register("service-worker3.js"); navigator.serviceWorker.addEventListener("message", function (event) { // 接受數(shù)據(jù),并填充在 DOM 中 showArea.innerHTML = showArea.innerHTML + ("
3. 更新 Service Worker
每次用戶導(dǎo)航至使用 Service Worker 的站點時,瀏覽器會嘗試在后臺重新下載該腳本文件。這時新的 Service Worker 將會在后臺安裝,并在第二次訪問時獲取控制權(quán),為了不與新的 Service Worker 緩存的文件沖突,我們可以使用類似 caches.open("v2") 語句來創(chuàng)建新的緩存目錄。
this.addEventListener("install", function(event) { event.waitUntil( // 創(chuàng)建新的緩存目錄,并指定 caches.open("v2").then(function(cache) { return cache.addAll([ "/sw-test/", "/sw-test/index.html", … ]); }); ); });
當(dāng)新的 Service Worker 激活,記得刪除 v1 緩存目錄,代碼如下。
this.addEventListener("activate", function(event) { // 聲明緩存白名單,該名單內(nèi)的緩存目錄不會被生成 var cacheWhitelist = ["v2"]; event.waitUntil( // 傳給 waitUntil() 的 promise 會阻塞其他的事件,直到它完成 // 確保清理操作會在第一次 fetch 事件之前完成 caches.keys().then(function(keyList) { return Promise.all(keyList.map(function(key) { if (cacheWhitelist.indexOf(key) === -1) { return caches.delete(key); } })); }) ); });
4. 預(yù)緩存
Service Worker 也可以在后臺主動發(fā)送請求,優(yōu)化用戶體驗,圖片來源于《餓了么的 PWA 升級實踐》。
5. Service Worker 支持的所有事件
五、銷毀 Service Worker
瀏覽器決定是否銷毀 Service Worker。在無痕瀏覽中,當(dāng)頁面關(guān)閉時相應(yīng)的 Service Worker 會被銷毀,因此盡量不要在代碼中留存全局變量。可以訪問 chrome://inspect/#service-workers和 chrome://serviceworker-internals/ 來檢查 Service Worker 是否已經(jīng)停用。
小結(jié)困擾 Web 用戶多年的難題——丟失網(wǎng)絡(luò)連接,從 APPCache 到 Service Worker,解決辦法一直在完善。Service Worker 開啟的服務(wù)工作線程,對如何步入 Web 應(yīng)用開發(fā)之旅,提供了很棒的切入角度。
那么,如何從本文開始,更好的學(xué)習(xí) Service Worker?結(jié)合更多其它技術(shù)博客與 Service Worker 的 API 文檔會更好。本文圖片素材、寫作思路多取源于此。
接口列表 | |
---|---|
Cache | CacheStorage |
Client | Clients |
ExtendableEvent | FetchEvent |
InstallEvent | Navigator.serviceWorker |
NotificationEvent | PeriodicSyncEvent |
PeriodicSyncManager | PeriodicSyncRegistration |
ServiceWorker | ServiceWorkerContainer |
ServiceWorkerGlobalScope | ServiceWorkerRegistration |
SyncEvent | SyncManager |
SyncRegistration | WindowClient |
本文所有關(guān)于 Web Worker、Service Worker 的代碼,持續(xù)更新在我的 gist 中:
https://gist.github.com/hyler...
Hello,我是韓亦樂,現(xiàn)任本科軟工男一枚。軟件工程專業(yè)的一路學(xué)習(xí)中,我有很多感悟,也享受持續(xù)分享的過程。如果想了解更多或能及時收到我的最新文章,歡迎訂閱我的個人微信號:韓亦樂。我的簡書個人主頁中,有我的訂閱號二維碼和 Github 主頁地址;[我的知乎主頁]中也會堅持產(chǎn)出,歡迎關(guān)注。
本文內(nèi)部編號經(jīng)由我的 Github 相關(guān)倉庫統(tǒng)一管理;本文可能發(fā)布在多個平臺但僅在上述倉庫中長期維護(hù);本文同時采用【知識共享署名-非商業(yè)性使用-禁止演繹 4.0 國際許可協(xié)議】進(jìn)行許可。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/88793.html
摘要:站點在同一瀏覽器中被訪問至少兩次,兩次訪問間隔至少為分鐘。詢問授權(quán)發(fā)送給后端存儲服務(wù)端向發(fā)送消息,同時帶上根據(jù)再下發(fā)給對應(yīng)的瀏覽器觸發(fā)的事件后續(xù)處理參考使用發(fā)送推送 關(guān)鍵特性 Web App Manifest – 在主屏幕添加app圖標(biāo),定義手機標(biāo)題欄顏色之類 App Shell – 先顯示APP的主結(jié)構(gòu),再填充主數(shù)據(jù),更快顯示更好體驗 Service Worker - 緩存,離線開...
背景 隨著前端技術(shù)日新月異地快速發(fā)展,web應(yīng)用功能和體驗也逐漸發(fā)展到可以和原生應(yīng)用媲美的程度,前端緩存技術(shù)的應(yīng)用對這起到了不可磨滅的貢獻(xiàn),因此想一探前端的緩存技術(shù),這篇文章主要會介紹在日常開發(fā)中比較少接觸的IndexedDB IndexedDB 什么是IndexedDB IndexedDB簡單理解就是前端數(shù)據(jù)庫,提供了一種在用戶瀏覽器中持久存儲數(shù)據(jù)的方法,但是和前端關(guān)系型數(shù)據(jù)不同的是,Index...
摘要:前端每周清單專注前端領(lǐng)域內(nèi)容,以對外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點分為新聞熱點開發(fā)教程工程實踐深度閱讀開源項目巔峰人生等欄目。 前端每周清單專注前端領(lǐng)域內(nèi)容,以對外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點;分為新聞熱點、開發(fā)教程、工程實踐、深度閱讀、開源項目、巔峰人生等欄目。歡迎關(guān)注【前端之巔】微信公眾號(ID:frontshow),及時獲取前端每周清單;本文則是對于...
閱讀 1221·2021-09-22 15:26
閱讀 2817·2021-09-09 11:52
閱讀 2151·2021-09-02 09:52
閱讀 2389·2021-08-12 13:28
閱讀 1332·2019-08-30 15:53
閱讀 658·2019-08-29 13:47
閱讀 3550·2019-08-29 11:00
閱讀 3241·2019-08-29 10:58