摘要:可能會(huì)延長(zhǎng)這些的壽命假設(shè)你有以下的這個(gè)緩存了和如果命中了緩存,就從緩存中取,否則發(fā)起網(wǎng)絡(luò)請(qǐng)求如果我們更改了,我們會(huì)修改中的版本號(hào),觸發(fā)的更新。
本文翻譯自:https://jakearchibald.com/201...這是一篇2016年的老文章。作者是Chrome瀏覽器的開(kāi)發(fā)成員。
本文首發(fā)于公眾號(hào):符合預(yù)期的CoyPan
使用正確的緩存可以帶來(lái)巨大的頁(yè)面性能上的收益,節(jié)省帶寬,減少服務(wù)器成本。但是許多網(wǎng)站并沒(méi)有解決好他們的緩存問(wèn)題,創(chuàng)造了一個(gè)race conditions,導(dǎo)致相互依賴的資源之間失去了同步。
絕大多數(shù)緩存的最佳實(shí)踐,都屬于下面兩種模式:
模式一:不可變的內(nèi)容 ,長(zhǎng)時(shí)間的max-ageCache-Control: max-age = 31536000
同一個(gè)URL對(duì)應(yīng)的內(nèi)容永不改變
瀏覽器/CDN 可以緩存這個(gè)資源長(zhǎng)達(dá)一年的時(shí)間
被緩存資源的存儲(chǔ)時(shí)間小于max-age指定的秒數(shù)時(shí),該資源可以直接被使用而無(wú)需經(jīng)過(guò)服務(wù)器。
在這種模式下,你不會(huì)去改變特定url下的文件內(nèi)容,你直接改變url:
每一個(gè)URL都包含一個(gè)跟隨文件內(nèi)容變換的部分。這個(gè)部分可以是版本號(hào),修改日期,或者文件內(nèi)容的hash值。
大多數(shù)服務(wù)端框架都有工具可以簡(jiǎn)單的實(shí)現(xiàn)這個(gè)需求。Node.js下還有更輕量級(jí)的工具能夠做到同樣的事情,比如gulp-rev.
但是,這種模式不適合諸如文章、博客這樣的場(chǎng)景。文章和博客的URL是不會(huì)有版本號(hào)的,而且他們的內(nèi)容能夠隨時(shí)修改。說(shuō)真的,如果我在文章中犯了拼寫(xiě)或者語(yǔ)法錯(cuò)誤,那么我需要能夠快速、頻繁的修改文章內(nèi)容。
模式二:可變的內(nèi)容,總是向服務(wù)器發(fā)起校驗(yàn)Cache-Control: no-cache
同一個(gè)url對(duì)應(yīng)的內(nèi)容會(huì)改變
任何本地緩存的版本都是不可信的,除非服務(wù)器校驗(yàn)通過(guò)
注意:no-cache并不意味著不緩存,而是使用緩存前必須請(qǐng)求服務(wù)端進(jìn)行檢查(或者說(shuō)叫重新校驗(yàn))。no-store告訴瀏覽器,根本不要緩存這個(gè)文件。同時(shí),must-revalidate也不是說(shuō)就『must-revalidate』,而是如果本地資源的緩存時(shí)間還沒(méi)有超過(guò)設(shè)置的max-age的值,就可以直接使用本地資源,否則必須重新校驗(yàn)。
在這種模式下,你可以在響應(yīng)頭里添加一個(gè)ETag(你選擇的版本ID)或者Last-Modified。客戶端下一次請(qǐng)求資源時(shí),會(huì)分別帶上If-None-Match和If-Modified-Since,服務(wù)端會(huì)判斷說(shuō):直接使用你已有的本地資源吧,他們是最新的。這就是最常見(jiàn)的:HTTP 304
如果沒(méi)有帶上ETag/Last-Modified,服務(wù)端會(huì)再次返回完成的內(nèi)容。
這種模式總是會(huì)發(fā)起一個(gè)網(wǎng)絡(luò)請(qǐng)求,而模式一是可以不用通過(guò)網(wǎng)絡(luò)的。
使用模式一時(shí),因?yàn)榫W(wǎng)絡(luò)基礎(chǔ)建設(shè)而導(dǎo)致的延時(shí)是很常見(jiàn)的,使用模式二時(shí),也很容易遇到網(wǎng)絡(luò)環(huán)境帶來(lái)的延遲。取而代之的是中間的東西:一個(gè)短時(shí)間的max-age設(shè)置和可變的內(nèi)容。這是一種十分糟糕的妥協(xié)。
對(duì)可變內(nèi)容使用max-age通常是一個(gè)錯(cuò)誤的選擇不幸的是,這種做法并非不常見(jiàn)。比如,Github pages就是這樣的。
想象一下有以下三個(gè)url:
/article/
/styles.css
/scripts.js
服務(wù)端都是返回的:
Cache-Control: must-revalidate, max-age=600
url對(duì)應(yīng)的內(nèi)容是變了
如果瀏覽器緩存了一個(gè)資源版本,但是沒(méi)有到10分鐘,會(huì)不經(jīng)過(guò)服務(wù)器直接使用這個(gè)緩存的資源。
否則發(fā)起一個(gè)網(wǎng)絡(luò)請(qǐng)求,帶上If-Modified-Since或者If-None-Match(如果可用)
這種模式在測(cè)試的時(shí)候看起來(lái)是可以的,但在現(xiàn)實(shí)中,會(huì)出問(wèn)題,并且很難追蹤。在上面的例子中,服務(wù)端確實(shí)已經(jīng)更新了HTML, CSS 和JS,但是頁(yè)面最終使用了緩存里的HTML,JS,CSS卻是從服務(wù)端獲取的最新的版本。資源版本不匹配導(dǎo)致了頁(yè)面出錯(cuò)。
通常情況下,當(dāng)我們對(duì)HTML進(jìn)行重大更改時(shí),我們還可能更改HTML對(duì)應(yīng)的CSS結(jié)構(gòu),并更新JS以適應(yīng)樣式和內(nèi)容的更改。這些資源是相互依賴的,但是緩存的header是無(wú)法描述這種依賴的。用戶最終看到的,可能是一兩個(gè)新版本的資源,和其他老的資源。
max-age和響應(yīng)時(shí)間有關(guān),因此,如果上述所有的資源都是在同一次訪問(wèn)中請(qǐng)求的,他們大概會(huì)在同一時(shí)間到期,但是仍然有很小的可能發(fā)生競(jìng)爭(zhēng)。如果你的某些頁(yè)面并不包含JS或者包含了不同的CSS,那么過(guò)期時(shí)間可能就不同步了。更糟糕的是,更糟糕的是,瀏覽器總是從緩存中刪除東西,它不知道HTML、CSS和JS是相互依賴的,所以它會(huì)很高興地刪除一個(gè)而不是其他的。上述的情況,都可能會(huì)導(dǎo)致頁(yè)面資源的版本不匹配。
對(duì)用戶來(lái)說(shuō),他們最終會(huì)看到錯(cuò)誤的頁(yè)面布局和錯(cuò)誤的頁(yè)面功能,從細(xì)微的錯(cuò)誤到完全不可用的內(nèi)容。
謝天謝地,對(duì)用戶來(lái)說(shuō)還是有補(bǔ)救措施的。
刷新可能會(huì)修復(fù)這個(gè)問(wèn)題
如果頁(yè)面作為刷新的一部分加載,瀏覽器會(huì)忽略max-age,向服務(wù)器進(jìn)行驗(yàn)證。因此,如果用戶遭遇了因?yàn)閙ax-age而造成的錯(cuò)誤,刷新是可以解決問(wèn)題的。當(dāng)然,強(qiáng)迫用戶這樣做會(huì)降低信任度,因?yàn)檫@會(huì)讓你感覺(jué)到你的網(wǎng)站是不靠譜的。
service worker可能會(huì)延長(zhǎng)這些bug的壽命
假設(shè)你有以下的service worker:
const version = "2"; self.addEventListener("install", event => { event.waitUntil( caches.open(`static-${version}`) .then(cache => cache.addAll([ "/styles.css", "/script.js" ])) ); }); self.addEventListener("activate", event => { // …delete old caches… }); self.addEventListener("fetch", event => { event.respondWith( caches.match(event.request) .then(response => response || fetch(event.request)) ); });
這個(gè)service-worker
緩存了script和style
如果命中了緩存,就從緩存中取,否則發(fā)起網(wǎng)絡(luò)請(qǐng)求
如果我們更改了CSS/JS,我們會(huì)修改service-worker中的版本號(hào),觸發(fā)service-worker的更新。但是,假如addAll發(fā)出的請(qǐng)求經(jīng)過(guò)了HTTP緩存(和其他大多數(shù)緩存一樣),我們也會(huì)進(jìn)入到max-age的race condition,緩存不匹配的CSS、JS版本。
一旦他們被緩存了,我們將會(huì)一直看到不匹配的CSS和JS,直到我們下一次更新service-worker。而在下一次更新時(shí),我們可能還會(huì)陷入另一個(gè)race condition。
你可以在service worker中跳過(guò)緩存:
self.addEventListener("install", event => { event.waitUntil( caches.open(`static-${version}`) .then(cache => cache.addAll([ new Request("/styles.css", { cache: "no-cache" }), new Request("/script.js", { cache: "no-cache" }) ])) ); });
不幸的是,這個(gè)緩存的設(shè)置在Chrome/Opera中還不支持,F(xiàn)irefox也是剛剛支持。你可以自己來(lái)實(shí)現(xiàn)類似的功能:
self.addEventListener("install", event => { event.waitUntil( caches.open(`static-${version}`) .then(cache => Promise.all( [ "/styles.css", "/script.js" ].map(url => { // cache-bust using a random query string return fetch(`${url}?${Math.random()}`).then(response => { // fail on 404, 500 etc if (!response.ok) throw Error("Not ok"); return cache.put(url, response); }) }) )) ); });
在上述代碼中,我用隨機(jī)數(shù)來(lái)避免緩存,但是你可以更進(jìn)一步,在構(gòu)建的時(shí)候?yàn)閮?nèi)容增加一個(gè)hash值(和sw-precache做的事差不多)。這是一種在js層面的對(duì)模式一的實(shí)現(xiàn),但是僅僅對(duì)service worker的使用者是有效的,而不是對(duì)所有的瀏覽器和你的CDN都有效。
service worker & http緩存可以同時(shí)使用,不要讓他們沖突正如你所見(jiàn),你可以繞過(guò)service worker中糟糕的緩存,但是你最好解決根源的問(wèn)題。正確的設(shè)置緩存能夠讓你在使用service worker的時(shí)候更加輕松,并且對(duì)那些不支持service worker的瀏覽器也是有好處的,還能讓你充分的使用你的CDN。
正確的緩存頭還意味著你可以大量簡(jiǎn)化server worker的更新:
const version = "23"; self.addEventListener("install", event => { event.waitUntil( caches.open(`static-${version}`) .then(cache => cache.addAll([ "/", "/script-f93bca2c.js", "/styles-a837cb1e.css", "/cats-0e9a2ef4.jpg" ])) ); });
在這里,我將使用模式2(服務(wù)器重新驗(yàn)證)緩存根頁(yè)面,其余資源使用模式1(不可變內(nèi)容)。每次service worker更新都將觸發(fā)對(duì)根頁(yè)面的請(qǐng)求,但只有當(dāng)資源的URL發(fā)生更改時(shí),才會(huì)下載其余資源。這很好,因?yàn)闊o(wú)論你是從以前的版本還是第10個(gè)版本更新,它都可以節(jié)省帶寬并提高性能。
相對(duì)于本地應(yīng)用來(lái)說(shuō),這是一個(gè)巨大的優(yōu)勢(shì)。在本地應(yīng)用中,不管二進(jìn)制內(nèi)容有細(xì)微和巨大的改變,整個(gè)二進(jìn)制內(nèi)容都會(huì)被下載。而在這里,我們只需要一個(gè)小小的下載,就能更新巨大的web app.
service worker的工作最好是作為一個(gè)增強(qiáng)方案,而不是變通方案。所以預(yù)期與緩存抗?fàn)?,不如好好利用緩存?/p> 謹(jǐn)慎使用,max-age & 可變內(nèi)容 也可以很有效
對(duì)于可變內(nèi)容使用max-age一般情況下是一個(gè)錯(cuò)誤的選擇,但也不總是這樣。比如,這個(gè)頁(yè)面設(shè)置了一個(gè)3分鐘的max-age. race condition在這個(gè)頁(yè)面是不會(huì)成為問(wèn)題的,因?yàn)檫@個(gè)頁(yè)面沒(méi)有任何遵循這一種模式的依賴(我的css,js,圖片等都遵循模式1-不可變內(nèi)容),依賴于此頁(yè)的任何內(nèi)容都不會(huì)遵循相同的模式。
這種模式意味著,如果我有幸寫(xiě)了一篇熱門文章,我的cdn可以讓我的服務(wù)器散熱,而我能忍受用戶需要花三分鐘時(shí)間才看到文章更新。
這種模式不能隨便使用。如果我在文章中添加了一個(gè)新的部分,并且將這個(gè)部分鏈接到一篇新的文章,那么我就創(chuàng)造了一個(gè)會(huì)爭(zhēng)用的依賴項(xiàng)。用戶可以單擊鏈接,并在沒(méi)有引用部分的情況下獲取文章的副本。如果我想避免這種情況,我就得更新第一篇文章,刷新cdn, 等待3分鐘,然后在另一篇文章中添加指向他的鏈接。是的…..你必須非常小心這種模式。
正確使用,緩存能極大的提高性能并且較少帶寬消耗。對(duì)于任何容易更改的URL,都支持不可變的內(nèi)容,否則在服務(wù)器重新驗(yàn)證時(shí)會(huì)使其安全。只有當(dāng)你足夠勇敢,并且你確信你沒(méi)有可能會(huì)失去同步的依賴項(xiàng)時(shí),再使用max-age和可變內(nèi)容的模式。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/62070.html
摘要:大部分服務(wù)器端的框架會(huì)有一些工具來(lái)讓這件事情變得簡(jiǎn)單,可是,這種方式并不適用于一些文章或者博客之類的網(wǎng)站,這些網(wǎng)站的不能通過(guò)版本號(hào)來(lái)管理,而且內(nèi)容也是經(jīng)常需要改變的。指不允許緩存。 本文譯自: 這里 本文已同步到我的博客 引言 緩存利用得當(dāng)?shù)脑?,有很大益處,比如?jié)省帶寬,降低服務(wù)器壓力等,但是很多網(wǎng)站沒(méi)能夠很好地利用緩存,造成一些相互依賴的資源出現(xiàn)不同步的情況。 關(guān)于緩存的處理方案主要...
摘要:前端靜態(tài)資源緩存最優(yōu)解以及的陷阱合理的使用緩存可以極大地提高網(wǎng)站的性能優(yōu)勢(shì),還可以節(jié)約帶寬從而降低服務(wù)器成本。此處注意和與第一天請(qǐng)求的版本號(hào)不同。既支持版本號(hào)類型的靜態(tài)資源緩存方式也支持服務(wù)器重新認(rèn)證的方式。 前端靜態(tài)資源緩存最優(yōu)解以及max-age的陷阱 合理的使用緩存可以極大地提高網(wǎng)站的性能優(yōu)勢(shì),還可以節(jié)約帶寬從而降低服務(wù)器成本。但是很多站點(diǎn)有只弄對(duì)了一半或者一半都沒(méi)有,如果是這樣...
摘要:業(yè)界動(dòng)態(tài)發(fā)布版本,同時(shí)發(fā)布了版本以及首個(gè)穩(wěn)定版本的。程序人生如何用人類的方式進(jìn)行二關(guān)于如何在中進(jìn)行良好的溝通,避免陷入一些潛在的陷阱。技術(shù)周刊由小組出品,匯聚一周好文章,周刊原文。 業(yè)界動(dòng)態(tài) Angular 5.1 & More Now Available Angular發(fā)布5.1版本,同時(shí)發(fā)布了Angular CLI 1.6版本以及首個(gè)穩(wěn)定版本的Angular Material。CL...
摘要:記錄以下資源備忘也就是本文譯文標(biāo)題為意譯,原標(biāo)題為,恐有不當(dāng),特此說(shuō)明。譯者注翻譯本文時(shí)譯者使用的確實(shí)無(wú)法看到信息,建議使用最新金絲雀版本一探究竟。應(yīng)用程序不應(yīng)當(dāng)依賴于服務(wù)器推送的可用性及其使用。 本文轉(zhuǎn)載自:眾成翻譯譯者:文藺鏈接:http://www.zcfy.cc/article/883原文:https://blog.cloudflare.com/http-2-server-pu...
閱讀 2586·2021-10-11 10:58
閱讀 1169·2021-09-29 09:34
閱讀 1530·2021-09-26 09:46
閱讀 3851·2021-09-22 15:31
閱讀 748·2019-08-30 15:54
閱讀 1472·2019-08-30 13:20
閱讀 1266·2019-08-30 13:13
閱讀 1499·2019-08-26 13:52