HTTP緩存看這一篇就夠了
?前言
HTTP緩存機(jī)制是優(yōu)化web性能的重要手段,也是優(yōu)化用戶體驗(yàn)的重要一環(huán)。了解和熟悉HTTP緩存機(jī)制也成為了前端工作者必不可少的技能。
HTTP緩存是用于臨時(shí)存儲(chǔ)網(wǎng)頁(yè)資源(如HTML頁(yè)面、圖像等),以減少服務(wù)器延遲的一種技術(shù)。HTTP緩存系統(tǒng)會(huì)保存下通過這套系統(tǒng)的文檔的副本;如果滿足某些條件,則可以由緩存滿足后續(xù)請(qǐng)求。HTTP緩存系統(tǒng)既可以指設(shè)備,也可以指計(jì)算機(jī)程序。
一、HTTP緩存的類別
HTTP緩存可分為強(qiáng)制緩存和協(xié)商緩存。
強(qiáng)制緩存:直接使用客戶端緩存,不從服務(wù)器拉取新資源,也不驗(yàn)證緩存資源是否過期。返回的狀態(tài)碼為200(OK)。
協(xié)商緩存:通過服務(wù)器驗(yàn)證資源有效性,資源有效則返回304(Not Modified),資源失效則返回最新的資源文件。
HTTP主流的有三個(gè)版本:HTTP/1.0、HTTP/1.1、HTTP/2.0。其中HTTP/1.0和HTTP/1.1的應(yīng)用最為廣泛。HTTP/2.0因?qū)彺鏅C(jī)制的改動(dòng)有別于HTTP/1.0和HTTP/1.1,因此HTTP/2.0相關(guān)內(nèi)容會(huì)在文末總結(jié)部分進(jìn)行介紹。HTTP/1.0與HTTP/1.1可根據(jù)緩存類別區(qū)分如下:
HTTP版本 | 強(qiáng)制緩存 | 協(xié)商緩存 |
HTTP/1.0 | Expires | Last-Modified |
HTTP/1.1 | Cache-Control | ETag |
二、主流的HTTP緩存參數(shù)
2.1 強(qiáng)制緩存
2.1.1 HTTP/1.0 - Expires
Expires?的值為服務(wù)端返回的到期時(shí)間,是一個(gè)GMT?(格林尼治標(biāo)準(zhǔn)時(shí)間)絕對(duì)時(shí)間,如:Tue, 17 Jan 2023 03:48:45 GMT?。下一次請(qǐng)求時(shí),客戶端判斷當(dāng)前系統(tǒng)GMT?時(shí)間是否小于緩存攜帶的GMT時(shí)間。若小于,直接使用緩存數(shù)據(jù),否則從服務(wù)器請(qǐng)求新的文件。
不過Expires存在的問題也顯而易見。
首先,使用客戶端獲取的GMT?時(shí)間與服務(wù)器GMT時(shí)間作比較,如果客戶端主動(dòng)修改了系統(tǒng)時(shí)間,就會(huì)出現(xiàn)緩存命中的誤差。
其次,GMT時(shí)間是基于格林尼治天文臺(tái)測(cè)算時(shí)間后,每隔一小時(shí)想全世界發(fā)放調(diào)時(shí)信息。觀測(cè)本身存在的誤差以及非實(shí)時(shí)的同步機(jī)制,都可能會(huì)導(dǎo)致出現(xiàn)緩存命中的誤差。
所以在HTTP/1.1版本中,使用Cache-Control?中的max-age替代。
2.1.2 HTTP/1.1 - Cache-Control
Cache-Control 是HTTP/1.1中重要的緩存規(guī)則。它可以在HTTP請(qǐng)求頭和響應(yīng)頭中使用,提供了多樣化的配置參數(shù)。同時(shí)也可以適用于更廣泛的復(fù)雜場(chǎng)景。
指令格式具有以下有效規(guī)則:
- 不區(qū)分大小寫,但建議使用小寫。
- 多個(gè)指令以逗號(hào)分隔。
- 具有可選參數(shù),可以用令牌或者帶引號(hào)的字符串語(yǔ)法。
常用的指令如下:
- no-store:不使用任何形式的緩存。具有HTTP緩存的最高優(yōu)先級(jí)。
- no-cache:不使用強(qiáng)制緩存。每次進(jìn)行響應(yīng)前都向服務(wù)器進(jìn)行緩存有效性驗(yàn)證。
- public:公共緩存。任何從源服務(wù)器到客戶端中的每個(gè)節(jié)點(diǎn)都可以對(duì)資源進(jìn)行緩存。
- private:私有緩存。僅客戶端可以對(duì)資源進(jìn)行緩存。
- max-age:客戶端緩存存儲(chǔ)的最長(zhǎng)時(shí)間,單位秒。判斷的優(yōu)先級(jí)高于Expires?,客戶端會(huì)判斷資源已緩存的時(shí)長(zhǎng)是否小于設(shè)置的max-age?時(shí)長(zhǎng)。是則直接使用緩存數(shù)據(jù),否則會(huì)進(jìn)行Expires的判斷流程。
- s-maxage:代理緩存服務(wù)器最長(zhǎng)的緩存時(shí)間,單位秒。優(yōu)先級(jí)高于max-age和Expires,僅適用于緩存服務(wù)器。
2.2 協(xié)商緩存
客戶端緩存失效后會(huì)向服務(wù)器進(jìn)行進(jìn)行緩存有效性驗(yàn)證,這個(gè)緩存有效性驗(yàn)證的過程就是協(xié)商緩存?。若資源有效,則返回304(Not Modified)?。客戶端拿到304狀態(tài)碼后會(huì)再?gòu)谋镜鼐彺嬷蝎@取資源。整個(gè)請(qǐng)求響應(yīng)過程是與無(wú)緩存流程一樣的。相對(duì)于無(wú)緩存流程的優(yōu)勢(shì)在于僅響應(yīng)狀態(tài)碼后,客戶端直接從本地緩存獲取文件,而無(wú)需進(jìn)行文件下載。減少了網(wǎng)絡(luò)響應(yīng)的文件大小,進(jìn)而加快了網(wǎng)絡(luò)響應(yīng)速度。
協(xié)商緩存的請(qǐng)求和響應(yīng)是需要相互配合的,可組合使用。如下表:
版本/階段 | 請(qǐng)求 | 響應(yīng) |
HTTP/1.0 | If-Modified-Since/If-Unmodified-Since | Last-Modified |
HTTP/1.1 | If-None-Match/If-Match | ETag |
協(xié)商緩存會(huì)先判斷請(qǐng)求頭中是否攜帶no-store。如果攜帶,則直接返回最新的服務(wù)器文件。
2.2.1 HTTP/1.0 - Last-Modified
客戶端第一次向服務(wù)器請(qǐng)求資源時(shí),服務(wù)器會(huì)返回資源。同時(shí)會(huì)在響應(yīng)頭中添加Last-Modified?字段來(lái)表明資源的最后修改時(shí)間。當(dāng)客戶端強(qiáng)制緩存失效后,會(huì)重新向服務(wù)器進(jìn)行緩存有效性驗(yàn)證。在驗(yàn)證的請(qǐng)求頭中,會(huì)添加If-Modified-Since?字段。服務(wù)器會(huì)對(duì)請(qǐng)求頭中的If-Modified-Since?和其存儲(chǔ)的資源Last-Modified?進(jìn)行比較。若If-Modified-Since?的時(shí)間不小于Last-Modified?,則資源有效,返回304(Not Modified)?。否則返回資源本身,并且重新記錄文件的Last-Modified。
Last-Modified?:響應(yīng)頭攜帶的資源最后修改時(shí)間。格式為last-modified:GMT。
If-Modified-Since?:請(qǐng)求頭攜帶的資源是否在某個(gè)時(shí)間后有修改。服務(wù)器會(huì)使用此值和其本身存儲(chǔ)的時(shí)間進(jìn)行比較。格式為:If-Modified-Since:GMT?。只可以用在 GET? 或 HEAD請(qǐng)求中。
If-Unmodified-Since?:請(qǐng)求頭攜帶的資源是否在某個(gè)時(shí)間后沒有修改。格式為:if-unmodified-since:GMT? 。有別于If-Modified-Since,If-Unmodified-Since?被用于POST?或其他非簡(jiǎn)單請(qǐng)求。如果在If-Unmodified-Since?指定的時(shí)間內(nèi)有過修改,則返回412(Precondition Failed)。
Last-Modified也是存在嚴(yán)重問題的。
首先,Last-Modified只關(guān)注文件的最后修改時(shí)間,和文件內(nèi)容無(wú)關(guān)。所以文件內(nèi)容在修改后又重新恢復(fù),也會(huì)導(dǎo)致文件的最后修改時(shí)間改變。此時(shí)客戶端的請(qǐng)求則無(wú)法使用緩存。
其次,Last-Modified?只能監(jiān)聽到秒級(jí)別的文件修改,如果文件在1秒內(nèi)進(jìn)行了多次修改,那么響應(yīng)頭返回的Last-Modified?的時(shí)間是不變的。此時(shí)客戶端因接收到響應(yīng)304,會(huì)導(dǎo)致資源無(wú)法及時(shí)更新,使用緩存的資源文件。
因此HTTP/1.1使用了ETag來(lái)進(jìn)行緩存協(xié)商。
2.2.1 HTTP/1.1 - ETag
為了解決上述Last-Modified?可能存在的不準(zhǔn)確的問題,HTTP/1.1推出了新的響應(yīng)字段ETag?來(lái)進(jìn)行協(xié)商緩存。ETag?的優(yōu)先級(jí)比Last-Modified高。
服務(wù)器接收到瀏覽器請(qǐng)求后,會(huì)先進(jìn)行If-None-Match與ETag?值的比較。若相等,則資源有效,返回304(Not Modified)?。否則返回資源本身,并且重新記錄文件的ETag。
ETag?:響應(yīng)頭攜帶的資源標(biāo)識(shí)符。格式為ETag:ETag-value可由服務(wù)器自行設(shè)置算法生成,通常是使用內(nèi)容的散列或簡(jiǎn)單的使用版本號(hào)。
If-None-Match?:請(qǐng)求頭攜帶的是否無(wú)匹配文件字段。優(yōu)先級(jí)高于Last-Modified?。當(dāng)服務(wù)器沒有任何資源的ETag?與請(qǐng)求頭攜帶的ETag值完全一樣時(shí),返回最新的資源,否則服務(wù)器會(huì)返回304。
If-Match?:請(qǐng)求頭攜帶的是否存在匹配文件字段。對(duì)于簡(jiǎn)單請(qǐng)求需要搭配 Range?首部使用。對(duì)于非簡(jiǎn)單請(qǐng)求,如PUT?,可用于上傳ETag。
三、總結(jié)
通過前文,我們了解到 HTTP 緩存主要分:強(qiáng)制緩存、協(xié)商緩存。強(qiáng)制緩存由Exipres(HTTP/1.0)、 Cache-Control(HTTP/1.1)控制??蛻舳酥苯幼x本地緩存,不會(huì)再跟服務(wù)器端交互,狀態(tài)碼 200。
協(xié)商緩存由 Last-Modified / If-Modified-Since(HTTP/1.0), Etag /If-None-Match(HTTP/1.1)進(jìn)行有效性驗(yàn)證,每次請(qǐng)求需要讓服務(wù)器判斷一下資源是否更新過,從而決定客戶端是否使用緩存,如果是,則返回 304,否則返回最新文件。
HTTP/2.0中設(shè)計(jì)了新的緩存方式,服務(wù)器推送(Push Server)。有別于強(qiáng)制緩存和協(xié)商緩存,屬于推送緩存。這種新的緩存方式主要是為了解決客戶端緩存時(shí)效性的問題,即還沒有收到客戶端的請(qǐng)求,服務(wù)器就把各種資源推送給客戶端。比如,客戶端只請(qǐng)求了a.html,但是服務(wù)器把a(bǔ).html、a.css、a.png全部發(fā)送給客戶端。這樣的話,只需要一次請(qǐng)求,客戶端就更新了所有文件的緩存,提高了緩存的時(shí)效性。
參考:
GMT(維基百科):https://en.wikipedia.org/wiki/Greenwich_Mean_Time
HTTP緩存(MDN):https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Caching