「前置知識點(diǎn)」,只是做一個概念的介紹,不會做深度解釋。因?yàn)椋@些概念在下面文章中會有出現(xiàn),為了讓行文更加的順暢,所以將本該在文內(nèi)的概念解釋放到前面來。「如果大家對這些概念熟悉,可以直接忽略」同時,由于閱讀我文章的群體有很多,所以有些知識點(diǎn)可能「我視之若珍寶,爾視只如草芥,棄之如敝履」。以下知識點(diǎn),請「酌情使用」。
要查看正在運(yùn)行的Service workers列表,我們可以在Chrome/Chromium中地址欄中輸入chrome://serviceworker-internals/。
圖片
chrome://xx 包含了很多內(nèi)置的功能,這塊也是有很大的說道的。后期,會單獨(dú)有一個專題來講。(已經(jīng)在籌劃準(zhǔn)備中....)
Cache API為緩存的 Request / Response 對象對提供存儲機(jī)制。例如,作為ServiceWorker 生命周期的一部分
Cache API像 workers 一樣,是暴露在 window 作用域下的。盡管它被定義在 service worker 的標(biāo)準(zhǔn)中,但是它不必一定要配合 service worker 使用。
「一個域可以有多個命名 Cache 對象」。我們需要在腳本 (例如,在 ServiceWorker 中) 中處理緩存更新的方式。
緩存配額使用估算值,可以使用 StorageEstimate API 獲得。
瀏覽器盡其所能去管理磁盤空間,但它有可能刪除一個域下的緩存數(shù)據(jù)。
瀏覽器要么自動刪除特定域的全部緩存,要么全部保留。
一些圍繞service worker緩存的重要 API 方法包括:
Cache.put, Cache.add和Cache.addAll只能在GET請求下使用。
更多詳情可以參考MDN-Cache[1]
如果我們以前沒有使用過Cache接口,可能會認(rèn)為它與 HTTP 緩存相同,或者至少與 HTTP 緩存相關(guān)。但實(shí)際情況并非如此。
可以將瀏覽器緩存看作是「分層的」。
Service workers是JavaScript層面的 API,「充當(dāng) Web 瀏覽器和 Web 服務(wù)器之間的代理」。它們的目標(biāo)是通過提供離線訪問以及提升頁面性能來提高可靠性。
Service workers是對現(xiàn)有網(wǎng)站的增強(qiáng)。這意味著如果使用Service workers的網(wǎng)站的用戶使用不支持Service workers的瀏覽器訪問網(wǎng)站,基本功能不會受到破壞。它是向下兼容的。
Service workers通過類似于桌面應(yīng)用程序的生命周期逐漸增強(qiáng)網(wǎng)站。想象一下當(dāng)從應(yīng)用商城安裝APP時會發(fā)生流程:
Service worker也采用類似的生命周期,但采用「漸進(jìn)增強(qiáng)」的方法。
Service worker技術(shù)中不可或缺的一部分是Cache API,這是一種「完全獨(dú)立于 HTTP 緩存的緩存機(jī)制」。Cache API可以在Service worker作用域內(nèi)和「主線程」作用域內(nèi)訪問。該特性為用戶操作與 Cache 實(shí)例的交互提供了許多可能性。
這意味著可以根據(jù)網(wǎng)站的特有的邏輯來緩存網(wǎng)絡(luò)請求的響應(yīng)。例如:
這些都是緩存策略的應(yīng)用方向。緩存策略使離線體驗(yàn)成為可能,并「通過繞過 HTTP 緩存觸發(fā)的高延遲重新驗(yàn)證檢查提供更好的性能」。
在「網(wǎng)絡(luò)上傳輸數(shù)據(jù)本質(zhì)上是異步的」。請求資產(chǎn)、服務(wù)器響應(yīng)請求以及下載響應(yīng)都需要時間。所涉及的時間是多樣且不確定的。Service workers通過「事件驅(qū)動」的 API 來適應(yīng)這種異步性,「使用回調(diào)處理事件」,例如:
都可以使用addEventListener API 注冊事件。所有這些事件都可以與Cache API進(jìn)行交互。特別是在網(wǎng)絡(luò)請求是離散的,運(yùn)行回調(diào)的能力對于「提供所期望的可靠性和速度」至關(guān)重要。
在JavaScript中進(jìn)行異步工作涉及使用Promises。因?yàn)镻romises也支持async和await,這些JavaScript特性也可用于簡化Service worker代碼,從而提供更好的開發(fā)者體驗(yàn)。
Service worker與Cache實(shí)例之間的交互涉及兩個不同的緩存概念:
預(yù)緩存是需要提前緩存資源的過程,通常在Service worker「安裝期間」進(jìn)行。通過預(yù)緩存,「關(guān)鍵的靜態(tài)資產(chǎn)和離線訪問所需的材料可以被下載并存儲在 Cache 實(shí)例中」。這種類型的緩存還可以提高需要預(yù)緩存資源的后續(xù)頁面的頁面速度。
運(yùn)行時緩存是指在運(yùn)行時從網(wǎng)絡(luò)請求資源時應(yīng)用緩存策略。這種類型的緩存非常有用,因?yàn)樗WC了用戶已經(jīng)訪問過的頁面和資源的離線訪問。
當(dāng)在Service worker中使用這些方法時,可以為用戶體驗(yàn)提供巨大的好處,并為普通的網(wǎng)頁提供類似應(yīng)用程序的行為。
Service workers與Web workers類似,它們的「所有工作都在自己的線程上進(jìn)行」。這意味著Service workers的任務(wù)不會與主線程上的其他任務(wù)競爭。
我們就以Web Worker為例子,做一個簡單的演示 在JavaScript中創(chuàng)建Web Worker并不是一項(xiàng)復(fù)雜的任務(wù)。
創(chuàng)建一個新的JavaScript文件,其中包含我們希望在工作線程中運(yùn)行的代碼。此文件不應(yīng)包含對DOM的任何引用,因?yàn)樗鼘o法訪問DOM。
在我們的主JavaScript文件中,使用Worker構(gòu)造函數(shù)創(chuàng)建一個新的Worker對象。此構(gòu)造函數(shù)接受一個參數(shù),即我們在第1步中創(chuàng)建的JavaScript文件的URL。
const worker = new Worker('worker.js');
為Worker對象添加事件偵聽器,以處理主線程和工作線程之間發(fā)送的消息。onmessage事件處理程序用于處理從工作線程發(fā)送的消息,而postMessage方法用于向工作線程發(fā)送消息。
worker.onmessage = function(event) { console.log('Worker said: ' + event.data);};worker.postMessage('Hello, worker!');
在我們的工作線程JavaScript文件中,添加一個事件偵聽器,以處理從主線程發(fā)送的消息,使用self對象的onmessage屬性。我們可以使用event.data屬性訪問消息中發(fā)送的數(shù)據(jù)。
self.onmessage = function(event) { console.log('Main thread said: ' + event.data); self.postMessage('Hello, main thread!');};
現(xiàn)在讓我們運(yùn)行Web應(yīng)用程序并測試Worker。我們應(yīng)該在控制臺中看到打印的消息,指示主線程和工作線程之間已發(fā)送和接收消息。
圖片
在深入了解service worker的生命周期之前,我們先來了解一下與生命周期運(yùn)作相關(guān)的「術(shù)語」(黑話)
了解service worker運(yùn)作方式的關(guān)鍵在于理解「控制」(control)。
一個service worker的作用域由其「在 Web 服務(wù)器上的位置確定」。如果一個service worker在位于/A/index.html的頁面上運(yùn)行,并且位于/A/sw.js上,那么該service worker的作用域就是/A/。
作用域限制了service worker控制的頁面。在上面的例子中,這意味著從/subdir/sw.js加載的service worker只能「控制位于/subdir/或其子頁面中」。
控制頁面的service worker仍然可以「攔截任何網(wǎng)絡(luò)請求」,包括跨域資源的請求。作用域限制了由service worker控制的頁面。
上述是默認(rèn)情況下作用域工作的方式,但可以通過設(shè)置Service-Worker-Allowed響應(yīng)頭,以及通過向register方法傳遞作用域選項(xiàng)來進(jìn)行覆蓋。
除非有很好的理由將service worker的作用域限制為origin的子集,否則應(yīng)「從 Web 服務(wù)器的根目錄加載service worker,以便其作用域盡可能廣泛」,不必?fù)?dān)心Service-Worker-Allowed頭部。
當(dāng)說一個service worker正在控制一個頁面時,實(shí)際上「是在控制一個客戶端」。客戶端是指URL位于該service worker作用域內(nèi)的「任何打開的頁面」。具體來說,這些是WindowClient的實(shí)例。
圖片
為了使service worker能夠控制頁面,首先必須將其部署。
讓我們看看一個沒有service worker的網(wǎng)站到部署全新service worker時,中間發(fā)生了啥?
注冊是service worker生命周期的「初始步驟」:
<script> // 直到頁面完全加載后再注冊service worker window.addEventListener("load", () => { // 檢查service worker是否可用 if ("serviceWorker" in navigator) { navigator.serviceWorker .register("/sw.js") .then(() => { console.log("Service worker 注冊成功!"); }) .catch((error) => { console.warn("注冊service worker時發(fā)生錯誤:"); console.warn(error); }); } });</script>
此代碼在「主線程」上運(yùn)行,并執(zhí)行以下操作:
還有一些關(guān)鍵要點(diǎn):
一旦注冊完成,「安裝」就開始了。
service worker在注冊后觸發(fā)其install事件。install「只會在每個service worker中調(diào)用一次,直到它被更新才會再次觸發(fā)」。可以使用addEventListener在worker的作用域內(nèi)注冊install事件的回調(diào):
// /sw.jsself.addEventListener("install", (event) => { const cacheKey = "前端柒八九_v1"; event.waitUntil( caches.open(cacheKey).then((cache) => { // 將數(shù)組中的所有資產(chǎn)添加到'前端柒八九_v1'的`Cache`實(shí)例中以供以后使用。 return cache.addAll([ "/css/global.bc7b80b7.css", "/css/home.fe5d0b23.css", "/js/home.d3cc4ba4.js", "/js/A.43ca4933.js", ]); }) );});
這會創(chuàng)建一個新的Cache實(shí)例并對資產(chǎn)進(jìn)行「預(yù)緩存」。其中有一個event.waitUntil。event.waitUntil接受一個Promise,并等待該P(yáng)romise被解決。
在這個示例中,這個Promise執(zhí)行兩個異步操作:
如果傳遞給event.waitUntil的Promise被「拒絕,安裝將失敗」。如果發(fā)生這種情況,service worker將被「丟棄」。
如果Promise被解決,安裝成功,service worker的狀態(tài)將更改為installed,然后進(jìn)入「激活」階段。
如果注冊和安裝成功,service worker將被「激活」,其狀態(tài)將變?yōu)閍ctivating。在service worker的activate事件中可以進(jìn)行激活期間的工作。在此事件中的一個典型任務(wù)是「清理舊緩存」,但對于「全新 service worker」,目前還不相關(guān)。
對于新的service worker,「安裝成功后,激活會立即觸發(fā)」。一旦激活完成,service worker的狀態(tài)將變?yōu)閍ctivated。
默認(rèn)情況下,新的service worker直到「下一次導(dǎo)航或頁面刷新之前才會開始控制頁面」。
一旦部署了第一個service worker,它很可能需要在以后進(jìn)行更新。例如,如果請求處理或預(yù)緩存邏輯發(fā)生了變化,就可能需要進(jìn)行更新。
瀏覽器會在以下情況下檢查service worker的更新:
了解瀏覽器何時更新service worker很重要,但“如何”也很重要。假設(shè)service worker的URL或作用域未更改,「只有在其內(nèi)容發(fā)生變化時,當(dāng)前安裝的service worker才會更新到新版本」。
瀏覽器以幾種方式檢測變化:
為確保瀏覽器能夠可靠地檢測service worker內(nèi)容的變化,「不要使用 HTTP 緩存保留它,也不要更改其文件名」。當(dāng)導(dǎo)航到service worker作用域內(nèi)的新頁面時,瀏覽器會自動執(zhí)行更新檢查。
關(guān)于更新,注冊邏輯通常不應(yīng)更改。然而,一個例外情況可能是「網(wǎng)站上的會話持續(xù)時間很長」。這可能在「單頁應(yīng)用程序」中發(fā)生,因?yàn)閷?dǎo)航請求通常很少,應(yīng)用程序通常在應(yīng)用程序生命周期的開始遇到一個導(dǎo)航請求。在這種情況下,可以在「主線程上手動觸發(fā)更新」:
navigator.serviceWorker.ready.then((registration) => { registration.update();});
對于傳統(tǒng)的網(wǎng)站,或者在用戶會話不持續(xù)很長時間的任何情況下,手動更新可能不是必要的。
當(dāng)使用打包工具生成「靜態(tài)資源」時,這些資源的「名稱中會包含哈希值」,例如framework.3defa9d2.js。假設(shè)其中一些資源被預(yù)緩存以供以后離線訪問,這將需要對service worker進(jìn)行更新以預(yù)緩存新的資源:
self.addEventListener("install", (event) => { const cacheKey = "前端柒八九_v2"; event.waitUntil( caches.open(cacheKey).then((cache) => { // 將數(shù)組中的所有資產(chǎn)添加到'前端柒八九_v2'的`Cache`實(shí)例中以供以后使用。 return cache.addAll([ "/css/global.ced4aef2.css", "/css/home.cbe409ad.css", "/js/home.109defa4.js", "/js/A.38caf32d.js", ]); }) );});
與之前的install事件示例有兩個方面不同:
更新后的service worker會與先前的service worker并存。這意味著舊的service worker仍然控制著任何打開的頁面。剛才安裝的新的service worker進(jìn)入等待狀態(tài),直到被激活。
默認(rèn)情況下,新的service worker將在「沒有任何客戶端由舊的service worker控制時激活」。這發(fā)生在相關(guān)網(wǎng)站的所有打開標(biāo)簽都關(guān)閉時。
當(dāng)安裝了新的service worker并結(jié)束了等待階段時,它會被激活,并丟棄舊的service worker。在更新后的service worker的activate事件中執(zhí)行的常見任務(wù)是「清理舊緩存」。通過使用caches.keys獲取所有打開的 Cache 實(shí)例的key,并使用caches.delete刪除不在允許列表中的所有舊緩存:
self.addEventListener("activate", (event) => { // 指定允許的緩存密鑰 const cacheAllowList = ["前端柒八九_v2"]; // 獲取當(dāng)前活動的所有`Cache`實(shí)例。 event.waitUntil( caches.keys().then((keys) => { // 刪除不在允許列表中的所有緩存: return Promise.all( keys.map((key) => { if (!cacheAllowList.includes(key)) { return caches.delete(key); } }) ); }) );});
舊的緩存不會自動清理。我們需要自己來做,否則可能會超過存儲配額。
由于第一個service worker中的前端柒八九_v1已經(jīng)過時,緩存允許列表已更新為指定前端柒八九_v2,這將刪除具有不同名稱的緩存。
「激活事件在舊緩存被刪除后完成」。此時,新的service worker將控制頁面,最終替代舊的service worker!
要有效使用service worker,有必要采用一個或多個緩存策略,這需要對Cache API有一定的了解。
緩存策略是service worker的fetch事件與Cache API之間的交互。如何編寫緩存策略取決于不同情況。
緩存策略的另一個重要的用途就是與service worker的fetch事件配合使用。我們已經(jīng)聽說過一些關(guān)于「攔截網(wǎng)絡(luò)請求」的內(nèi)容,而service worker內(nèi)部的fetch事件就是處理這種情況的:
// 建立緩存名稱const cacheName = "前端柒八九_v1";self.addEventListener("install", (event) => { event.waitUntil(caches.open(cacheName));});self.addEventListener("fetch", async (event) => { // 這是一個圖片請求 if (event.request.destination === "image") { // 打開緩存 event.respondWith( caches.open(cacheName).then((cache) => { // 從緩存中響應(yīng)圖片,如果緩存中沒有,就從網(wǎng)絡(luò)獲取圖片 return cache.match(event.request).then((cachedResponse) => { return ( cachedResponse || fetch(event.request.url).then((fetchedResponse) => { // 將網(wǎng)絡(luò)響應(yīng)添加到緩存以供將來訪問。 // 注意:我們需要復(fù)制響應(yīng)以保存在緩存中,同時使用原始響應(yīng)作為請求的響應(yīng)。 cache.put(event.request, fetchedResponse.clone()); // 返回網(wǎng)絡(luò)響應(yīng) return fetchedResponse; }) ); }); }) ); } else { return; }});
上面的代碼執(zhí)行以下操作:
fetch事件的事件對象包含一個request屬性,其中包含一些有用的信息,可幫助我們識別每個請求的類型:
「異步操作是關(guān)鍵」。我們還記得install事件提供了一個event.waitUntil方法,它接受一個promise,并在激活之前等待其解析。fetch事件提供了類似的event.respondWith方法,我們可以使用它來返回異步fetch請求的結(jié)果或Cache接口的match方法返回的響應(yīng)。
展示了從頁面到service worker到緩存的流程。
「僅緩存」運(yùn)作方式:當(dāng)service worker控制頁面時,「匹配的請求只會進(jìn)入緩存」。這意味著為了使該模式有效,「任何緩存的資源都需要在安裝時進(jìn)行預(yù)緩存」,而「這些資源在service worker更新之前將不會在緩存中進(jìn)行更新」。
// 建立緩存名稱const cacheName = "前端柒八九_v1";// 要預(yù)緩存的資產(chǎn)const preCachedAssets = ["/A.jpg", "/B.jpg", "/C.jpg", "/D.jpg"];self.addEventListener("install", (event) => { // 在安裝時預(yù)緩存資產(chǎn) event.waitUntil( caches.open(cacheName).then((cache) => { return cache.addAll(preCachedAssets); }) );});self.addEventListener("fetch", (event) => { const url = new URL(event.request.url); const isPrecachedRequest = preCachedAssets.includes(url.pathname); if (isPrecachedRequest) { // 從緩存中獲取預(yù)緩存的資產(chǎn) event.respondWith( caches.open(cacheName).then((cache) => { return cache.match(event.request.url); }) ); } else { // 轉(zhuǎn)到網(wǎng)絡(luò) return; }});
在上面的示例中,數(shù)組中的資產(chǎn)在安裝時被預(yù)緩存。當(dāng)service worker處理fetch請求時,我們「檢查fetch事件處理的請求 URL 是否在預(yù)緩存資產(chǎn)的數(shù)組中」。
圖片
「僅網(wǎng)絡(luò)」的策略與「僅緩存」相反,它將請求通過service worker傳遞到網(wǎng)絡(luò),而「不與 service worker 緩存進(jìn)行任何交互」。這是一種「確保內(nèi)容新鮮度」的好策略,但其權(quán)衡是「當(dāng)用戶離線時將無法正常工作」。
要確保請求直接通過到網(wǎng)絡(luò),只需「不對匹配的請求調(diào)用 event.respondWith」。如果我們想更明確,可以在要傳遞到網(wǎng)絡(luò)的請求的fetch事件回調(diào)中加入一個空的return;。這就是「僅緩存」策略演示中對于未經(jīng)預(yù)緩存的請求所發(fā)生的情況。
圖片
對于「匹配的請求」,流程如下:
// 建立緩存名稱const cacheName = "前端柒八九_v1";self.addEventListener("fetch", (event) => { // 檢查這是否是一個圖像請求 if (event.request.destination === "image") { event.respondWith( caches.open(cacheName).then((cache) => { // 首先從緩存中獲取 return cache.match(event.request.url).then((cachedResponse) => { // 如果我們有緩存的響應(yīng),則返回緩存的響應(yīng) if (cachedResponse) { return cachedResponse; } // 否則,訪問網(wǎng)絡(luò) return fetch(event.request).then((fetchedResponse) => { // 將網(wǎng)絡(luò)響應(yīng)添加到緩存以供以后訪問 cache.put(event.request, fetchedResponse.clone()); // 返回網(wǎng)絡(luò)響應(yīng) return fetchedResponse; }); }); }) ); } else { return; }});
盡管這個示例只涵蓋了圖像,但這是一個很好的范例,「適用于所有靜態(tài)資產(chǎn)」(如CSS、JavaScript、圖像和字體),「尤其是哈希版本的資產(chǎn)」。它「通過跳過 HTTP 緩存可能啟動的任何與服務(wù)器的內(nèi)容新鮮度檢查,為不可變資產(chǎn)提供了速度提升」。更重要的是,「任何緩存的資產(chǎn)都將在離線時可用」。
它的含義就是:
這種策略對于HTML或 API 請求非常有用,當(dāng)在線時,我們希望獲取資源的最新版本,但希望在離線時能夠訪問最新可用的版本。
// 建立緩存名稱const cacheName = "前端柒八九_v1";self.addEventListener("fetch", (event) => { // 檢查這是否是導(dǎo)航請求 if (event.request.mode === "navigate") { // 打開緩存 event.respondWith( caches.open(cacheName).then((cache) => { // 首先通過網(wǎng)絡(luò)請求 return fetch(event.request.url) .then((fetchedResponse) => { cache.put(event.request, fetchedResponse.clone()); return fetchedResponse; }) .catch(() => { // 如果網(wǎng)絡(luò)不可用,從緩存中獲取 return cache.match(event.request.url); }); }) ); } else { return; }});
在需要重視離線功能,但又需要平衡該功能與獲取一些標(biāo)記或 API 數(shù)據(jù)的最新版本的情況下,「網(wǎng)絡(luò)優(yōu)先,備用緩存」是一種實(shí)現(xiàn)這一目標(biāo)的可靠策略。
圖片
「陳舊時重新驗(yàn)證」策略是其中最復(fù)雜的。該策略的過程「優(yōu)先考慮了資源的訪問速度」,同時在后臺保持其更新。該策略的工作流程如下:
這是一個適用于「需要保持更新但不是絕對必要的資源」的策略,比如網(wǎng)站的頭像。它們會在用戶愿意更新時進(jìn)行更新,但不一定需要在每次請求時獲取最新版本。
// 建立緩存名稱const cacheName = "前端柒八九_v1";self.addEventListener("fetch", (event) => { if (event.request.destination === "image") { event.respondWith( caches.open(cacheName).then((cache) => { return cache.match(event.request).then((cachedResponse) => { const fetchedResponse = fetch(event.request).then( (networkResponse) => { cache.put(event.request, networkResponse.clone()); return networkResponse; } ); return cachedResponse || fetchedResponse; }); }) ); } else { return; }});
如果將預(yù)緩存「應(yīng)用于太多的資產(chǎn)」,或者如果Service Worker在頁面「完成加載關(guān)鍵資產(chǎn)之前」就注冊了,那么可能會遇到問題。
當(dāng)Service Worker在「安裝期間預(yù)緩存資產(chǎn)時,將同時發(fā)起一個或多個網(wǎng)絡(luò)請求」。如果時機(jī)不合適,這可能會對用戶體驗(yàn)產(chǎn)生問題。即使時機(jī)剛剛好,如果未對預(yù)緩存資產(chǎn)的「數(shù)量進(jìn)行限制」,仍可能會浪費(fèi)數(shù)據(jù)。
如果Service Worker預(yù)緩存任何內(nèi)容,那么它的注冊時機(jī)很重要。Service Worker通常使用內(nèi)聯(lián)的<script>元素注冊。這意味著 HTML 解析器可能在頁面的關(guān)鍵資產(chǎn)加載完成之前就發(fā)現(xiàn)了Service Worker的注冊代碼。
這是一個問題。Service Worker在最壞的情況下應(yīng)該對性能沒有不利影響,而不是使性能變差。為用戶著想,應(yīng)該在「頁面加載事件」觸發(fā)時注冊Service Worker。這減少了預(yù)緩存可能干擾加載頁面的關(guān)鍵資產(chǎn)的機(jī)會,從而意味著頁面可以更快地實(shí)現(xiàn)交互,而無需處理后來可能不需要的資產(chǎn)的網(wǎng)絡(luò)請求。
if ("serviceWorker" in navigator) { window.addEventListener("load", function () { navigator.serviceWorker.register("/service-worker.js"); });}
無論時機(jī)如何,「預(yù)緩存都涉及發(fā)送網(wǎng)絡(luò)請求」。如果不謹(jǐn)慎地選擇要預(yù)緩存的資產(chǎn)清單,結(jié)果可能會浪費(fèi)一些數(shù)據(jù)。
「浪費(fèi)數(shù)據(jù)是預(yù)緩存的一個潛在代價」,但并非每個人都可以訪問快速的互聯(lián)網(wǎng)或無限的數(shù)據(jù)計(jì)劃!「在預(yù)緩存時,應(yīng)考慮刪除特別大的資產(chǎn),并依賴于運(yùn)行時緩存來捕捉它們」,而不是進(jìn)行假設(shè)用戶都需要這些資源,從而全部都進(jìn)行緩存。
雖然Service Worker生命周期確保了可預(yù)測的安裝和更新過程,但它可能使本地開發(fā)與常規(guī)開發(fā)有些不同。
通常情況下,Service WorkerAPI 僅在通過 HTTPS 提供的頁面上可用,但是我們平時開發(fā)中,經(jīng)常是通過 localhost 提供的頁面進(jìn)行嚴(yán)重。
此時,我們可以通過 chrome://flags/#unsafely-treat-insecure-origin-as-secure,并指定要將不安全的起源視為安全起源。
迄今為止,測試Service Worker的最有效方法是依賴于無痕窗口,例如 Chrome 中的無痕窗口。每次打開無痕窗口時,我們都是從頭開始的。沒有活動的Service Worker,也沒有打開的緩存實(shí)例。這種測試的常規(guī)流程如下:
通過這個過程,我們模擬了Service Worker的生命周期。
Chrome DevTools 應(yīng)用程序面板中提供的其他測試工具也可以幫助,盡管它們可能在某些方面修改了Service Worker的生命周期。
圖片
應(yīng)用程序面板有一個名為Service Workers的面板,顯示了當(dāng)前頁面的活動Service Worker。每個活動Service Worker都可以手動更新,甚至完全注銷。面板頂部還有三個開關(guān)按鈕,有助于開發(fā)。
這些開關(guān)非常有幫助,特別是Bypass for network,當(dāng)我們正在開發(fā)一個具有活動Service Worker的項(xiàng)目時,同時還希望確保體驗(yàn)在沒有Service Worker的情況下也能按預(yù)期工作。
當(dāng)在本地開發(fā)中使用活動的Service Worker,而不需要更新后刷新或繞過網(wǎng)絡(luò)功能時,按住 Shift 鍵并單擊刷新按鈕也非常有用。
這個操作的鍵盤變體涉及在 macOS 計(jì)算機(jī)上按住 Shift、Cmd 和 R 鍵。
這被稱為「強(qiáng)制刷新」,它繞過 HTTP 緩存以獲取網(wǎng)絡(luò)數(shù)據(jù)。當(dāng)Service Worker處于活動狀態(tài)時,強(qiáng)制刷新也將完全繞過Service Worker。
如果不確定特定緩存策略是否按預(yù)期工作,或者希望從網(wǎng)絡(luò)獲取所有內(nèi)容以比較有Service Worker和無Service Worker時的行為,這個功能非常有用。更好的是,這是一個規(guī)定的行為,因此所有支持Service Worker的瀏覽器都會觀察到它。
如果無法檢查緩存,就很難確定緩存策略是否按預(yù)期工作。Chrome DevTools 的應(yīng)用程序面板提供了一個子面板,用于檢查緩存實(shí)例的內(nèi)容。
在DevTools中檢查緩存
這個子面板通過提供以下功能來使Service Worker開發(fā)變得更容易:
這個圖形用戶界面使檢查Service Worker緩存更容易,以查看項(xiàng)目是否已添加、更新或從Service Worker緩存中完全刪除。
在擁有大量大型靜態(tài)資產(chǎn)(如高分辨率圖像)的網(wǎng)站中,可能會觸及存儲配額。當(dāng)這種情況發(fā)生時,瀏覽器將從緩存中驅(qū)逐它認(rèn)為過時或值得犧牲以騰出空間以容納新資產(chǎn)的項(xiàng)目。
處理存儲配額應(yīng)該是Service Worker開發(fā)的一部分,而 Workbox 使這個過程比自行管理更簡單。不管是否使用 Workbox,模擬自定義存儲配額以測試緩存管理邏輯可能是一個不錯的主意。
存儲使用查看器
Chrome DevTools 的 Application 面板中的存儲使用查看器。在這里,正在設(shè)置自定義存儲配額。
Chrome DevTools 的 Application 面板有一個存儲子面板,提供了有關(guān)頁面使用的當(dāng)前存儲配額的信息。它還允許指定以兆字節(jié)為單位的自定義配額。一旦生效,Chrome 將執(zhí)行自定義存儲配額以進(jìn)行測試。
這個子面板還包含一個清除站點(diǎn)數(shù)據(jù)按鈕以及一整套相關(guān)的復(fù)選框,用于在單擊按鈕時清除哪些內(nèi)容。其中包括任何打開的緩存實(shí)例,以及注銷控制頁面的任何活動Service Worker的能力。
本文鏈接:http://www.tebozhan.com/showinfo-26-16137-0.htmlWorkBox 之底層邏輯Service Worker
聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。郵件:2376512515@qq.com
上一篇: JVM類加載機(jī)制分析