摘要:譯前端離線指南上原文鏈接作者緩存持久化為您的站點提供一定量的可用空間來執(zhí)行其所需的操作。這是可能的,因為通常會保持使內(nèi)容最具線性特性的順序。
[[譯]前端離線指南(上)](https://juejin.im/post/5c0788...緩存持久化原文鏈接:The offline cookbook 作者:Jake Archibald
為您的站點提供一定量的可用空間來執(zhí)行其所需的操作。該可用空間可在站點中所有存儲之間共享:LocalStorage、IndexedDB、Filesystem,當(dāng)然也包含Caches。
您能獲取到的空間容量是不一定的,同時由于設(shè)備和存儲條件的差異也會有所不同。您可以通過下面的代碼來查看您已獲得的空間容量:
navigator.storageQuota.queryInfo("temporary").then((info) => { console.log(info.quota); // Result:console.log(info.usage); // Result: });
然而,與所有瀏覽器存儲一樣,如果設(shè)備面臨存儲壓力,瀏覽器就會隨時舍棄這些存儲內(nèi)容。但遺憾的是,瀏覽器無法區(qū)分您珍藏的電影,和您沒啥興趣的游戲之間有啥區(qū)別。
為解決此問題,建議使用 requestPersistent API:
// 在頁面中運行 navigator.storage.requestPersistent().then((granted) => { if (granted) { // 啊哈,數(shù)據(jù)保存在這里呢 } });
當(dāng)然,用戶必須要授予權(quán)限。讓用戶參與進(jìn)這個流程是很有必要的,因為我們可以預(yù)期用戶會控制刪除。如果用戶手中的設(shè)備面臨存儲壓力,而且清除不重要的數(shù)據(jù)還沒能解決問題,那么用戶就需要根據(jù)自己的判斷來決定刪除哪些項目以及保留哪些項目。
為了實現(xiàn)此目的,需要操作系統(tǒng)將“持久化”源等同于其存儲使用空間細(xì)分中的本機(jī)應(yīng)用,而不是作為單個項目報告給瀏覽器。
緩存建議-響應(yīng)請求無論您打算緩存多少內(nèi)容,除非您告訴ServiceWorker應(yīng)當(dāng)在何時以及如何去緩存內(nèi)容,ServiceWorker不會去主動使用緩存。下面是幾種用于處理請求的策略。
僅緩存(cache only)適用于: 您認(rèn)為在站點的“該版本”中屬于靜態(tài)內(nèi)容的任何資源。您應(yīng)當(dāng)在install事件中就緩存這些資源,以便您可以在處理請求的時候依靠它們。
self.addEventListener("fetch", (event) => { // 如果某個匹配到的資源在緩存中找不到, // 則響應(yīng)結(jié)果看起來就會像一個連接錯誤。 event.respondWith(caches.match(event.request)); });
...盡管您一般不需要通過特殊的方式來處理這種情況,但“緩存,回退到網(wǎng)絡(luò)”涵蓋了這種策略。
僅網(wǎng)絡(luò)(network only)
適用于: 沒有相應(yīng)的離線資源的對象,比如analytics pings,非GET請求。
self.addEventListener("fetch", (event) => { event.respondWith(fetch(event.request)); // 或者簡單地不再調(diào)用event.respondWith,這樣就會 // 導(dǎo)致默認(rèn)的瀏覽器行為 });
...盡管您一般不需要通過特殊的方式來處理這種情況,但“緩存,回退到網(wǎng)絡(luò)”涵蓋了這種策略。
緩存優(yōu)先,若無緩存則回退到網(wǎng)絡(luò)(Cache, falling back to network)
適用于: 如果您以緩存優(yōu)先的方式構(gòu)建,那么這種策略就是您處理大多數(shù)請求時所用的策略 根據(jù)傳入請求而定,其他策略會有例外。
self.addEventListener("fetch", (event) => { event.respondWith(async function() { const response = await caches.match(event.request); return response || fetch(event.request); }()); });
其中,針對已緩存的資源提供“Cache only”的行為,針對未緩存的資源(包含所有非GET請求,因為它們根本無法被緩存)提供“Network only”的行為。
緩存與網(wǎng)絡(luò)競爭
適用于: 存儲在讀取速度慢的硬盤中的小型資源。
在老舊硬盤、病毒掃描程序、和較快網(wǎng)速這幾種因素都存在的情況下,從網(wǎng)絡(luò)中獲取資源可能比從硬盤中獲取的速度更快。不過,通過網(wǎng)絡(luò)獲取已經(jīng)在用戶設(shè)備中保存過的內(nèi)容,是一種浪費流量的行為,所以請牢記這一點。
// Promise.race 對我們來說并不太好,因為若當(dāng)其中一個promise在 // fulfilling之前reject了,那么整個Promise.race就會返回reject。 // 我們來寫一個更好的race函數(shù): function promiseAny(promises) { return new Promise((resolve, reject) => { // 確保promises代表所有的promise對象。 promises = promises.map(p => Promise.resolve(p)); // 只要當(dāng)其中一個promise對象調(diào)用了resolve,就讓此promise對象變成resolve的 promises.forEach(p => p.then(resolve)); // 如果傳入的所有promise都reject了,就讓此promise對象變成resject的 promises.reduce((a, b) => a.catch(() => b)) .catch(() => reject(Error("All failed"))); }); }; self.addEventListener("fetch", (event) => { event.respondWith( promiseAny([ caches.match(event.request), fetch(event.request) ]) ); });通過網(wǎng)絡(luò)獲取失敗回退到緩存(Network falling back to cache)
適用于: 對頻繁更新的資源進(jìn)行快速修復(fù)。例如:文章、頭像、社交媒體時間軸、游戲排行榜等。
這就意味著您可以為在線用戶提供最新內(nèi)容,但是離線用戶獲取到的是較老的緩存版本。如果網(wǎng)絡(luò)請求成功,您可能需要更新緩存。
不過,這種方法存在缺陷。如果用戶的網(wǎng)絡(luò)斷斷續(xù)續(xù),或者網(wǎng)速超慢,則用戶可能會在從自己設(shè)備中獲取更好的、可接受的內(nèi)容之前,花很長一段時間去等待網(wǎng)絡(luò)請求失敗。這樣的用戶體驗是非常糟糕的。請查看下一個更好的解決方案:“緩存然后訪問網(wǎng)絡(luò)”。
self.addEventListener("fetch", (event) => { event.respondWith(async function() { try { return await fetch(event.request); } catch (err) { return caches.match(event.request); } }()); });緩存然后訪問網(wǎng)絡(luò)
適用于: 更新頻繁的內(nèi)容。例如:文章、社交媒體時間軸、游戲排行榜等。
這種策略需要頁面發(fā)起兩個請求,一個是請求緩存,一個是請求網(wǎng)絡(luò)。首先展示緩存數(shù)據(jù),然后當(dāng)網(wǎng)絡(luò)數(shù)據(jù)到達(dá)的時候,更新頁面。
有時候,您可以在獲取到新的數(shù)據(jù)的時候,只替換當(dāng)前數(shù)據(jù)(比如:游戲排行榜),但是具有較大的內(nèi)容時將導(dǎo)致數(shù)據(jù)中斷?;旧现v,不要在用戶可能正在閱讀或正在操作的內(nèi)容突然“消失”。
Twitter在舊內(nèi)容上添加新內(nèi)容,并調(diào)整滾動的位置,以便讓用戶感知不到。這是可能的,因為 Twitter 通常會保持使內(nèi)容最具線性特性的順序。 我為 trained-to-thrill 復(fù)制了此模式,以盡快獲取屏幕上的內(nèi)容,但當(dāng)它出現(xiàn)時仍會顯示最新內(nèi)容。
頁面中的代碼
async function update() { // 盡可能地發(fā)起網(wǎng)絡(luò)請求 const networkPromise = fetch("/data.json"); startSpinner(); const cachedResponse = await caches.match("/data.json"); if (cachedResponse) await displayUpdate(cachedResponse); try { const networkResponse = await networkPromise; const cache = await caches.open("mysite-dynamic"); cache.put("/data.json", networkResponse.clone()); await displayUpdate(networkResponse); } catch (err) { } stopSpinner(); const networkResponse = await networkPromise; } async function displayUpdate(response) { const data = await response.json(); updatePage(data); }常規(guī)回退
如果您未能從網(wǎng)絡(luò)和緩存中提供某些資源,您可能需要一個常規(guī)回退策略。
適用于: 次要的圖片,比如頭像,失敗的POST請求,“離線時不可用”的頁面。
self.addEventListener("fetch", (event) => { event.respondWith(async function() { // 嘗試從緩存中匹配 const cachedResponse = await caches.match(event.request); if (cachedResponse) return cachedResponse; try { // 回退到網(wǎng)絡(luò) return await fetch(event.request); } catch (err) { // 如果都失敗了,啟用常規(guī)回退: return caches.match("/offline.html"); // 不過,事實上您需要根據(jù)URL和Headers,準(zhǔn)備多個不同回退方案 // 例如:頭像的兜底圖 } }()); });
您回退到的項目可能是一個“安裝依賴項”(見《前端離線指南(上)》中的“安裝時——以依賴的形式”小節(jié))。
ServiceWorker-side templating
適用于: 無法緩存其服務(wù)器響應(yīng)的頁面。
在服務(wù)器上渲染頁面可提高速度,但這意味著會包括在緩存中沒有意義的狀態(tài)數(shù)據(jù),例如,“Logged in as…”。如果您的頁面由 ServiceWorker 控制,您可能會轉(zhuǎn)而選擇請求 JSON 數(shù)據(jù)和一個模板,并進(jìn)行渲染。
importScripts("templating-engine.js"); self.addEventListener("fetch", (event) => { const requestURL = new URL(event.request); event.responseWith(async function() { const [template, data] = await Promise.all([ caches.match("/article-template.html").then(r => r.text()), caches.match(requestURL.path + ".json").then(r => r.json()), ]); return new Response(renderTemplate(template, data), { headers: {"Content-Type": "text/html"} }) }()); });總結(jié)
您不必只選擇其中的一種方法,您可以根據(jù)請求URL選擇使用多種方法。比如,在trained-to-thrill中使用了:
在安裝時緩存,適用于靜態(tài)UI
在網(wǎng)絡(luò)響應(yīng)時緩存,適用于網(wǎng)絡(luò)圖片和數(shù)據(jù)
從緩存中獲取,若失敗回退到網(wǎng)絡(luò),適用于大部分的請求
從緩存中獲取,然后請求網(wǎng)絡(luò),適用于網(wǎng)絡(luò)搜索結(jié)果
只需要根據(jù)請求,就能決定要做什么:
self.addEventListener("fetch", (event) => { // Parse the URL: const requestURL = new URL(event.request.url); // Handle requests to a particular host specifically if (requestURL.hostname == "api.example.com") { event.respondWith(/* some combination of patterns */); return; } // Routing for local URLs if (requestURL.origin == location.origin) { // Handle article URLs if (/^/article//.test(requestURL.pathname)) { event.respondWith(/* some other combination of patterns */); return; } if (requestURL.pathname.endsWith(".webp")) { event.respondWith(/* some other combination of patterns */); return; } if (request.method == "POST") { event.respondWith(/* some other combination of patterns */); return; } if (/cheese/.test(requestURL.pathname)) { event.respondWith( new Response("Flagrant cheese error", { status: 512 }) ); return; } } // A sensible default pattern event.respondWith(async function() { const cachedResponse = await caches.match(event.request); return cachedResponse || fetch(event.request); }()); });鳴謝
感謝下列諸君為本文提供那些可愛的圖標(biāo):
Code,作者:buzzyrobot
Calendar,作者:Scott Lewis
Network,作者:Ben Rizzo
SD,作者:Thomas Le Bas
CPU,作者:iconsmind.com
Trash,作者:trasnik
Notification,作者:@daosme
Layout,作者:Mister Pixel
Cloud,作者:P.J. Onori
同時感謝 Jeff Posnick 在我點擊“發(fā)布”按鈕之前,為我找到多處明顯錯誤。
擴(kuò)展閱讀Intro to ServiceWorkers
Is ServiceWorker ready? - 跟蹤主流瀏覽器對ServiceWorker的實現(xiàn)狀態(tài)
JavaScript promises, there and back again - Promise指南
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/99813.html
摘要:接受一個對象作參數(shù),來定義安裝時長和安裝是否成功,如果狀態(tài)為,則認(rèn)為此次安裝失敗,并且拋棄如果一個舊版本的正在運行,則它將保持不變。在頁面既可以在中獲取到,也可以在頁面中獲取到,這就意味著你不必一定要通過來向緩存中添加內(nèi)容。 原文鏈接:The offline cookbook作者:Jake Archibald 使用AppCache可以為我們提供幾種支持內(nèi)容離線工作的模式。如果這些模式正...
摘要:雖然有著各種各樣的不同,但是相同的是,他們前端優(yōu)化不完全指南前端掘金篇幅可能有點長,我想先聊一聊閱讀的方式,我希望你閱讀的時候,能夠把我當(dāng)作你的競爭對手,你的夢想是超越我。 如何提升頁面渲染效率 - 前端 - 掘金Web頁面的性能 我們每天都會瀏覽很多的Web頁面,使用很多基于Web的應(yīng)用。這些站點看起來既不一樣,用途也都各有不同,有在線視頻,Social Media,新聞,郵件客戶端...
摘要:是在谷歌的年開發(fā)者峰會上宣布,但穩(wěn)定的技術(shù)和工具終于在月到達(dá)。固然也不能保證蘋果將實施這項技術(shù),但這并不重要,你的應(yīng)用程序仍然可以在中工作,它只是不會從離線執(zhí)行中受益。我有一種感覺一旦上體驗有明顯提升蘋果將鼓勵支持。 2016年是值得紀(jì)念、奇怪的、有點歡騰/可怕的一年,取決于你的觀點。跟其他事件相比僅僅專注于JavaScript可能看起來無關(guān)緊要,但它是每個Web開發(fā)人員的工作生活中巨...
摘要:是在谷歌的年開發(fā)者峰會上宣布,但穩(wěn)定的技術(shù)和工具終于在月到達(dá)。固然也不能保證蘋果將實施這項技術(shù),但這并不重要,你的應(yīng)用程序仍然可以在中工作,它只是不會從離線執(zhí)行中受益。我有一種感覺一旦上體驗有明顯提升蘋果將鼓勵支持。 2016年是值得紀(jì)念、奇怪的、有點歡騰/可怕的一年,取決于你的觀點。跟其他事件相比僅僅專注于JavaScript可能看起來無關(guān)緊要,但它是每個Web開發(fā)人員的工作生活中巨...
摘要:是在谷歌的年開發(fā)者峰會上宣布,但穩(wěn)定的技術(shù)和工具終于在月到達(dá)。固然也不能保證蘋果將實施這項技術(shù),但這并不重要,你的應(yīng)用程序仍然可以在中工作,它只是不會從離線執(zhí)行中受益。我有一種感覺一旦上體驗有明顯提升蘋果將鼓勵支持。 2016年是值得紀(jì)念、奇怪的、有點歡騰/可怕的一年,取決于你的觀點。跟其他事件相比僅僅專注于JavaScript可能看起來無關(guān)緊要,但它是每個Web開發(fā)人員的工作生活中巨...
閱讀 6205·2021-11-22 15:32
閱讀 826·2021-11-11 16:54
閱讀 3164·2021-10-13 09:40
閱讀 2170·2021-09-03 10:35
閱讀 1838·2021-08-09 13:47
閱讀 1879·2019-08-30 15:55
閱讀 1939·2019-08-30 15:43
閱讀 2460·2019-08-29 17:06