摘要:譯前端離線指南上原文鏈接作者緩存持久化為您的站點提供一定量的可用空間來執行其所需的操作。這是可能的,因為通常會保持使內容最具線性特性的順序。
[[譯]前端離線指南(上)](https://juejin.im/post/5c0788...緩存持久化原文鏈接:The offline cookbook 作者:Jake Archibald
為您的站點提供一定量的可用空間來執行其所需的操作。該可用空間可在站點中所有存儲之間共享:LocalStorage、IndexedDB、Filesystem,當然也包含Caches。
您能獲取到的空間容量是不一定的,同時由于設備和存儲條件的差異也會有所不同。您可以通過下面的代碼來查看您已獲得的空間容量:
navigator.storageQuota.queryInfo("temporary").then((info) => { console.log(info.quota); // Result:console.log(info.usage); // Result: });
然而,與所有瀏覽器存儲一樣,如果設備面臨存儲壓力,瀏覽器就會隨時舍棄這些存儲內容。但遺憾的是,瀏覽器無法區分您珍藏的電影,和您沒啥興趣的游戲之間有啥區別。
為解決此問題,建議使用 requestPersistent API:
// 在頁面中運行 navigator.storage.requestPersistent().then((granted) => { if (granted) { // 啊哈,數據保存在這里呢 } });
當然,用戶必須要授予權限。讓用戶參與進這個流程是很有必要的,因為我們可以預期用戶會控制刪除。如果用戶手中的設備面臨存儲壓力,而且清除不重要的數據還沒能解決問題,那么用戶就需要根據自己的判斷來決定刪除哪些項目以及保留哪些項目。
為了實現此目的,需要操作系統將“持久化”源等同于其存儲使用空間細分中的本機應用,而不是作為單個項目報告給瀏覽器。
緩存建議-響應請求無論您打算緩存多少內容,除非您告訴ServiceWorker應當在何時以及如何去緩存內容,ServiceWorker不會去主動使用緩存。下面是幾種用于處理請求的策略。
僅緩存(cache only)適用于: 您認為在站點的“該版本”中屬于靜態內容的任何資源。您應當在install事件中就緩存這些資源,以便您可以在處理請求的時候依靠它們。
self.addEventListener("fetch", (event) => { // 如果某個匹配到的資源在緩存中找不到, // 則響應結果看起來就會像一個連接錯誤。 event.respondWith(caches.match(event.request)); });
...盡管您一般不需要通過特殊的方式來處理這種情況,但“緩存,回退到網絡”涵蓋了這種策略。
僅網絡(network only)
適用于: 沒有相應的離線資源的對象,比如analytics pings,非GET請求。
self.addEventListener("fetch", (event) => { event.respondWith(fetch(event.request)); // 或者簡單地不再調用event.respondWith,這樣就會 // 導致默認的瀏覽器行為 });
...盡管您一般不需要通過特殊的方式來處理這種情況,但“緩存,回退到網絡”涵蓋了這種策略。
緩存優先,若無緩存則回退到網絡(Cache, falling back to network)
適用于: 如果您以緩存優先的方式構建,那么這種策略就是您處理大多數請求時所用的策略 根據傳入請求而定,其他策略會有例外。
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”的行為。
緩存與網絡競爭
適用于: 存儲在讀取速度慢的硬盤中的小型資源。
在老舊硬盤、病毒掃描程序、和較快網速這幾種因素都存在的情況下,從網絡中獲取資源可能比從硬盤中獲取的速度更快。不過,通過網絡獲取已經在用戶設備中保存過的內容,是一種浪費流量的行為,所以請牢記這一點。
// Promise.race 對我們來說并不太好,因為若當其中一個promise在 // fulfilling之前reject了,那么整個Promise.race就會返回reject。 // 我們來寫一個更好的race函數: function promiseAny(promises) { return new Promise((resolve, reject) => { // 確保promises代表所有的promise對象。 promises = promises.map(p => Promise.resolve(p)); // 只要當其中一個promise對象調用了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) ]) ); });通過網絡獲取失敗回退到緩存(Network falling back to cache)
適用于: 對頻繁更新的資源進行快速修復。例如:文章、頭像、社交媒體時間軸、游戲排行榜等。
這就意味著您可以為在線用戶提供最新內容,但是離線用戶獲取到的是較老的緩存版本。如果網絡請求成功,您可能需要更新緩存。
不過,這種方法存在缺陷。如果用戶的網絡斷斷續續,或者網速超慢,則用戶可能會在從自己設備中獲取更好的、可接受的內容之前,花很長一段時間去等待網絡請求失敗。這樣的用戶體驗是非常糟糕的。請查看下一個更好的解決方案:“緩存然后訪問網絡”。
self.addEventListener("fetch", (event) => { event.respondWith(async function() { try { return await fetch(event.request); } catch (err) { return caches.match(event.request); } }()); });緩存然后訪問網絡
適用于: 更新頻繁的內容。例如:文章、社交媒體時間軸、游戲排行榜等。
這種策略需要頁面發起兩個請求,一個是請求緩存,一個是請求網絡。首先展示緩存數據,然后當網絡數據到達的時候,更新頁面。
有時候,您可以在獲取到新的數據的時候,只替換當前數據(比如:游戲排行榜),但是具有較大的內容時將導致數據中斷。基本上講,不要在用戶可能正在閱讀或正在操作的內容突然“消失”。
Twitter在舊內容上添加新內容,并調整滾動的位置,以便讓用戶感知不到。這是可能的,因為 Twitter 通常會保持使內容最具線性特性的順序。 我為 trained-to-thrill 復制了此模式,以盡快獲取屏幕上的內容,但當它出現時仍會顯示最新內容。
頁面中的代碼
async function update() { // 盡可能地發起網絡請求 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); }常規回退
如果您未能從網絡和緩存中提供某些資源,您可能需要一個常規回退策略。
適用于: 次要的圖片,比如頭像,失敗的POST請求,“離線時不可用”的頁面。
self.addEventListener("fetch", (event) => { event.respondWith(async function() { // 嘗試從緩存中匹配 const cachedResponse = await caches.match(event.request); if (cachedResponse) return cachedResponse; try { // 回退到網絡 return await fetch(event.request); } catch (err) { // 如果都失敗了,啟用常規回退: return caches.match("/offline.html"); // 不過,事實上您需要根據URL和Headers,準備多個不同回退方案 // 例如:頭像的兜底圖 } }()); });
您回退到的項目可能是一個“安裝依賴項”(見《前端離線指南(上)》中的“安裝時——以依賴的形式”小節)。
ServiceWorker-side templating
適用于: 無法緩存其服務器響應的頁面。
在服務器上渲染頁面可提高速度,但這意味著會包括在緩存中沒有意義的狀態數據,例如,“Logged in as…”。如果您的頁面由 ServiceWorker 控制,您可能會轉而選擇請求 JSON 數據和一個模板,并進行渲染。
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"} }) }()); });總結
您不必只選擇其中的一種方法,您可以根據請求URL選擇使用多種方法。比如,在trained-to-thrill中使用了:
在安裝時緩存,適用于靜態UI
在網絡響應時緩存,適用于網絡圖片和數據
從緩存中獲取,若失敗回退到網絡,適用于大部分的請求
從緩存中獲取,然后請求網絡,適用于網絡搜索結果
只需要根據請求,就能決定要做什么:
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); }()); });鳴謝
感謝下列諸君為本文提供那些可愛的圖標:
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 在我點擊“發布”按鈕之前,為我找到多處明顯錯誤。
擴展閱讀Intro to ServiceWorkers
Is ServiceWorker ready? - 跟蹤主流瀏覽器對ServiceWorker的實現狀態
JavaScript promises, there and back again - Promise指南
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/99813.html
摘要:接受一個對象作參數,來定義安裝時長和安裝是否成功,如果狀態為,則認為此次安裝失敗,并且拋棄如果一個舊版本的正在運行,則它將保持不變。在頁面既可以在中獲取到,也可以在頁面中獲取到,這就意味著你不必一定要通過來向緩存中添加內容。 原文鏈接:The offline cookbook作者:Jake Archibald 使用AppCache可以為我們提供幾種支持內容離線工作的模式。如果這些模式正...
摘要:雖然有著各種各樣的不同,但是相同的是,他們前端優化不完全指南前端掘金篇幅可能有點長,我想先聊一聊閱讀的方式,我希望你閱讀的時候,能夠把我當作你的競爭對手,你的夢想是超越我。 如何提升頁面渲染效率 - 前端 - 掘金Web頁面的性能 我們每天都會瀏覽很多的Web頁面,使用很多基于Web的應用。這些站點看起來既不一樣,用途也都各有不同,有在線視頻,Social Media,新聞,郵件客戶端...
摘要:是在谷歌的年開發者峰會上宣布,但穩定的技術和工具終于在月到達。固然也不能保證蘋果將實施這項技術,但這并不重要,你的應用程序仍然可以在中工作,它只是不會從離線執行中受益。我有一種感覺一旦上體驗有明顯提升蘋果將鼓勵支持。 2016年是值得紀念、奇怪的、有點歡騰/可怕的一年,取決于你的觀點。跟其他事件相比僅僅專注于JavaScript可能看起來無關緊要,但它是每個Web開發人員的工作生活中巨...
摘要:是在谷歌的年開發者峰會上宣布,但穩定的技術和工具終于在月到達。固然也不能保證蘋果將實施這項技術,但這并不重要,你的應用程序仍然可以在中工作,它只是不會從離線執行中受益。我有一種感覺一旦上體驗有明顯提升蘋果將鼓勵支持。 2016年是值得紀念、奇怪的、有點歡騰/可怕的一年,取決于你的觀點。跟其他事件相比僅僅專注于JavaScript可能看起來無關緊要,但它是每個Web開發人員的工作生活中巨...
摘要:是在谷歌的年開發者峰會上宣布,但穩定的技術和工具終于在月到達。固然也不能保證蘋果將實施這項技術,但這并不重要,你的應用程序仍然可以在中工作,它只是不會從離線執行中受益。我有一種感覺一旦上體驗有明顯提升蘋果將鼓勵支持。 2016年是值得紀念、奇怪的、有點歡騰/可怕的一年,取決于你的觀點。跟其他事件相比僅僅專注于JavaScript可能看起來無關緊要,但它是每個Web開發人員的工作生活中巨...
閱讀 6200·2021-11-22 15:32
閱讀 823·2021-11-11 16:54
閱讀 3162·2021-10-13 09:40
閱讀 2168·2021-09-03 10:35
閱讀 1833·2021-08-09 13:47
閱讀 1875·2019-08-30 15:55
閱讀 1938·2019-08-30 15:43
閱讀 2460·2019-08-29 17:06