瀏覽器對(duì)于請(qǐng)求資源, 流程如圖所示:
可以看到瀏覽器的緩存機(jī)制分為兩個(gè)部分:
1、當(dāng)前緩存是否過(guò)期?
2、服務(wù)器中的文件是否有改動(dòng)?
第一步:判斷當(dāng)前緩存是否過(guò)期
這是判斷是否啟用緩存的第一步。如果瀏覽器通過(guò)某些條件(條件之后再說(shuō))判斷出來(lái),ok現(xiàn)在這個(gè)緩存沒(méi)有過(guò)期可以用,那么連請(qǐng)求都不會(huì)發(fā)的,直接是啟用之前瀏覽器緩存下來(lái)的那份文件,此時(shí)狀態(tài)碼為200
第二步:判斷服務(wù)器中的文件是否有改動(dòng)
1、緩存過(guò)期,文件有改動(dòng),那么下載新文件,此時(shí)狀態(tài)碼為200
2、緩存過(guò)期,文件無(wú)改動(dòng),那么服務(wù)器只會(huì)給你返回一個(gè)頭信息(304),瀏覽器讀取304后,就會(huì)去讀取過(guò)期緩存文件。
如何判斷緩存的過(guò)期以及文件的變動(dòng)?
瀏覽器擁有一系列成熟的緩存策略. 按照發(fā)生的時(shí)間順序分別為存儲(chǔ)策略,?過(guò)期策略,?協(xié)商策略,? 其中存儲(chǔ)策略在收到響應(yīng)后應(yīng)用,?過(guò)期策略,?協(xié)商策略在發(fā)送請(qǐng)求前應(yīng)用. 流程圖如下所示.
判斷緩存過(guò)期,主要還是靠HTTP頭,廢話不多說(shuō), 我們先來(lái)看兩張表格
http header中與緩存有關(guān)的key
key描述存儲(chǔ)策略過(guò)期策略協(xié)商策略
Cache-Control指定緩存機(jī)制,覆蓋其它設(shè)置????
Pragmahttp1.0字段,指定緩存機(jī)制??
Expireshttp1.0字段,指定緩存的過(guò)期時(shí)間??
Last-Modified資源最后一次的修改時(shí)間??
ETag唯一標(biāo)識(shí)請(qǐng)求資源的字符串??
2.緩存協(xié)商策略用于重新驗(yàn)證緩存資源是否有效, 有關(guān)的key如下.
key描述
If-Modified-Since緩存校驗(yàn)字段, 值為資源最后一次的修改時(shí)間, 即上次收到的Last-Modified值
If-Unmodified-Since同上, 處理方式與之相反
If-Match緩存校驗(yàn)字段, 值為唯一標(biāo)識(shí)請(qǐng)求資源的字符串, 即上次收到的ETag值
If-None-Match同上, 處理方式與之相反
下面我們來(lái)看下各個(gè)頭域(key)的作用.
Cache-Control
瀏覽器緩存里, Cache-Control是金字塔頂尖的規(guī)則, 它藐視一切其他設(shè)置, 只要其他設(shè)置與其抵觸, 一律覆蓋之.
不僅如此, 它還是一個(gè)復(fù)合規(guī)則, 包含多種值, 橫跨?存儲(chǔ)策略,?過(guò)期策略?兩種, 同時(shí)在請(qǐng)求頭和響應(yīng)頭都可設(shè)置.
語(yǔ)法為:?“Cache-Control : cache-directive”.
?Cache-directive共有如下12種(其中請(qǐng)求中指令7種, 響應(yīng)中指令9種):
Cache-directive描述存儲(chǔ)策略過(guò)期策略請(qǐng)求字段響應(yīng)字段
public資源將被客戶端和代理服務(wù)器緩存????
private資源僅被客戶端緩存, 代理服務(wù)器不緩存????
no-store請(qǐng)求和響應(yīng)都不緩存??????
no-cache相當(dāng)于max-age:0,must-revalidate即資源被緩存, 但是緩存立刻過(guò)期, 同時(shí)下次訪問(wèn)時(shí)強(qiáng)制驗(yàn)證資源有效性????????
max-age緩存資源, 但是在指定時(shí)間(單位為秒)后緩存過(guò)期????????
s-maxage同上, 依賴public設(shè)置, 覆蓋max-age, 且只在代理服務(wù)器上有效.??????
max-stale指定時(shí)間內(nèi), 即使緩存過(guò)時(shí), 資源依然有效????
min-fresh緩存的資源至少要保持指定時(shí)間的新鮮期????
must-revalidation?/?proxy-revalidation如果緩存失效, 強(qiáng)制重新向服務(wù)器(或代理)發(fā)起驗(yàn)證(因?yàn)閙ax-stale等字段可能改變緩存的失效時(shí)間)????
only-if-cached僅僅返回已經(jīng)緩存的資源, 不訪問(wèn)網(wǎng)絡(luò), 若無(wú)緩存則返回504??
no-transform強(qiáng)制要求代理服務(wù)器不要對(duì)資源進(jìn)行轉(zhuǎn)換, 禁止代理服務(wù)器對(duì)?Content-Encoding,?Content-Range,?Content-Type字段的修改(因此代理的gzip壓縮將不被允許)????
假設(shè)所請(qǐng)求資源于4月5日緩存, 且在4月12日過(guò)期.
當(dāng)max-age 與 max-stale 和 min-fresh 同時(shí)使用時(shí), 它們的設(shè)置相互之間獨(dú)立生效, 最為保守的緩存策略總是有效. 這意味著, 如果max-age=10 days, max-stale=2 ? ? ? ? ? ?days, min-fresh=3 days, 那么:
根據(jù)max-age的設(shè)置, 覆蓋原緩存周期, ?緩存資源將在4月15日失效(5+10=15);
根據(jù)max-stale的設(shè)置, 緩存過(guò)期后兩天依然有效, 此時(shí)響應(yīng)將返回110(Response is stale)狀態(tài)碼, 緩存資源將在4月14日失效(12+2=14);
根據(jù)min-fresh的設(shè)置, 至少要留有3天的新鮮期, 緩存資源將在4月9日失效(12-3=9);
由于客戶端總是采用最保守的緩存策略, 因此, 4月9日后, 對(duì)于該資源的請(qǐng)求將重新向服務(wù)器發(fā)起驗(yàn)證.
技術(shù)細(xì)節(jié):must-revalidate,no-cache,max-age=0,no-store,? ? ? ? ? ? ?
must-revalidate:? ?如果你配置了max-age信息,當(dāng)緩存資源仍然新鮮(小于max-age)時(shí)使用緩存,否則需要對(duì)資源進(jìn)行驗(yàn)證。所以must-revalidate可以和max-age組合使用Cache-Control: must-revalidate, max-age=60
no-cache: 雖然字面意義是“不要緩存”。但它實(shí)際上的機(jī)制是,仍然對(duì)資源使用緩存,但每一次在使用緩存之前必須(MUST)向服務(wù)器對(duì)緩存資源進(jìn)行驗(yàn)證。
max-age=0:告知瀏覽器,資源已經(jīng)過(guò)期了,你應(yīng)該(SHOULD)對(duì)資源進(jìn)行重新驗(yàn)證了;在重新獲取資源之前,先檢驗(yàn)ETag/Last-Modified。而no-cache則是告訴瀏覽器在每一次使用緩存之前,你必須(MUST)對(duì)資源進(jìn)行重新驗(yàn)證。
區(qū)別在于:SHOULD是非強(qiáng)制性的,而MUST是強(qiáng)制性的。在no-cache的情況下,瀏覽器在向服務(wù)器驗(yàn)證成功之前絕不會(huì)使用過(guò)期的緩存資源,而max-age=0則不一定了。
no-store:? 不使用任何緩存。有趣的事情是,雖然no-cache意為對(duì)緩存進(jìn)行驗(yàn)證,但是因?yàn)榇蠹覐V泛的錯(cuò)誤的把它當(dāng)作no-store來(lái)使用,所以有的瀏覽器也就附和了這種設(shè)計(jì)。這是一個(gè)典型的劣幣驅(qū)逐良幣
不管是max-age=0還是no-cache,都會(huì)返回304(資源無(wú)修改的情況下),no-store才是真正的不進(jìn)行緩存。
public VS. private
要知道從服務(wù)器到瀏覽器之間并非只有瀏覽器能夠?qū)Y源進(jìn)行緩存,服務(wù)器的返回可能會(huì)經(jīng)過(guò)一些中間(intermediate)服務(wù)器甚至甚至專業(yè)的中間緩存服務(wù)器,還有CDN。而有些請(qǐng)求返回是用戶級(jí)別、是私人的,所以你可能不希望這些中間服務(wù)器緩存返回。此時(shí)你需要將Cache-Control設(shè)置為private以避免暴露。
所以綜上,關(guān)于如何設(shè)計(jì)緩存機(jī)制,還是要依據(jù)你的需求而定,可以通過(guò)下面的這棵決策樹決定:
Expires
Expires:Wed,?05?Apr?2017?00:55:35?GMT1
即到期時(shí)間, 以服務(wù)器時(shí)間為參考系, 其優(yōu)先級(jí)比?Cache-Control:max-age?低, 兩者同時(shí)出現(xiàn)在響應(yīng)頭時(shí),?Expires將被后者覆蓋.?如果Expires,?Cache-Control: max-age, 或?Cache-Control:s-maxage?都沒(méi)有在響應(yīng)頭中出現(xiàn), 并且也沒(méi)有其它緩存的設(shè)置, 那么瀏覽器默認(rèn)會(huì)采用一個(gè)啟發(fā)式的算法, 通常會(huì)取響應(yīng)頭的Date_value - Last-Modified_value值的10%作為緩存時(shí)間.
如下資源便采取了啟發(fā)式緩存算法.
其緩存時(shí)間為?(Date_value - Last-Modified_value) * 10%, 計(jì)算如下:
const?Date_value?=?new?Date('Thu,?06?Apr?2017?01:30:56?GMT').getTime();const?LastModified_value?=?new?Date('Thu,?01?Dec?2016?06:23:23?GMT').getTime();const?cacheTime?=?(Date_value?-?LastModified_value)?/?10;const?Expires_timestamp?=?Date_value?+?cacheTime;const?Expires_value?=?new?Date(Expires_timestamp);console.log('Expires:',?Expires_value);?//?Expires:?Tue?Apr?18?2017?23:25:41?GMT+0800?(CST)123456
可見該資源將于2017年4月18日23點(diǎn)25分41秒過(guò)期, 嘗試以下兩步進(jìn)行驗(yàn)證:
1) 試著把本地時(shí)間修改為2017年4月18日23點(diǎn)25分40秒, 迅速刷新頁(yè)面, 發(fā)現(xiàn)強(qiáng)緩存依然有效(依舊是200 OK (from disk cache)).
2) 然后又修改本地時(shí)間為2017年4月18日23點(diǎn)26分40秒(即往后撥1分鐘), 刷新頁(yè)面, 發(fā)現(xiàn)緩存已過(guò)期, 此時(shí)瀏覽器重新向服務(wù)器發(fā)起了驗(yàn)證, 且命中了304協(xié)商緩存, 如下所示.
3) 將本地時(shí)間恢復(fù)正常(即 2017-04-06 09:54:19). 刷新頁(yè)面, 發(fā)現(xiàn)Date依然是4月18日, 如下所示.
從?? Provisional headers are shown?和Date字段可以看出來(lái), 瀏覽器并未發(fā)出請(qǐng)求, 緩存依然有效, 只不過(guò)此時(shí)Status Code顯示為200 OK. ? ? ? ? ? ?(甚至我還專門打開了charles, 也沒(méi)有發(fā)現(xiàn)該資源的任何請(qǐng)求, 可見這個(gè)200 OK多少有些誤導(dǎo)人的意味)
可見, 啟發(fā)式緩存算法采用的緩存時(shí)間可長(zhǎng)可短, 因此對(duì)于常規(guī)資源, 建議明確設(shè)置緩存時(shí)間(如指定max-age 或 expires).
Expires VS. max-age
Expires和max-age都是用于控制緩存的生命周期。不同的是Expires指定的是過(guò)期的具體時(shí)間,例如Sun, 21 Mar 2027 08:52:14 ? ? ? ? ? ?GMT,而max-age指定的是生命時(shí)長(zhǎng)秒數(shù)315360000。
區(qū)別在于Expires是 HTTP/1.0 的中的標(biāo)準(zhǔn),而max-age是屬于Cache-Control的內(nèi)容,是 HTTP/1.1 中的定義的。但為了想向前兼容,這兩個(gè)屬性仍然要同時(shí)存在。
但有一種更傾向于使用max-age的觀點(diǎn)認(rèn)為Expires過(guò)于復(fù)雜了。例如上面的例子Sun, 21 Mar 2027 08:52:14 ? ? ? ? ? ?GMT,如果你在表示小時(shí)的數(shù)字缺少了一個(gè)0,則很有可能出現(xiàn)出錯(cuò);如果日期沒(méi)有轉(zhuǎn)換到用戶的正確時(shí)區(qū),則有可能出錯(cuò)。這里出錯(cuò)的意思可能包括但不限于緩存失效、緩存生命周期出錯(cuò)等。
判斷文件變動(dòng)
常用的方式為Etag和Last-Modified,思路上差不多,這里作者只介紹Last-Modified的用法。
Last-Modified方式需要用到兩個(gè)字段:Last-Modified & if-modified-since。
先來(lái)看下這兩個(gè)字段的形式:
Last-Modified : Fri , 12 May 2006 18:53:33 GMT
If-Modified-Since : Fri , 12 May 2006 18:53:33 GMT
可以看出其實(shí)形式是一樣的,就是一個(gè)標(biāo)準(zhǔn)時(shí)間。那么怎么用呢?來(lái)看下圖:
當(dāng)?shù)谝淮握?qǐng)求某一個(gè)文件的時(shí)候,就會(huì)傳遞回來(lái)一個(gè)Last-Modified 字段,其內(nèi)容是這個(gè)文件的修改時(shí)間。當(dāng)這個(gè)文件緩存過(guò)期,瀏覽器又向服務(wù)器請(qǐng)求這個(gè)文件的時(shí)候,會(huì)自動(dòng)帶一個(gè)請(qǐng)求頭字段If-Modified-Since,其值是上一次傳遞過(guò)來(lái)的Last-Modified的值,拿這個(gè)值去和服務(wù)器中現(xiàn)在這個(gè)文件的最后修改時(shí)間做對(duì)比,如果相等,那么就不會(huì)重新拉取這個(gè)文件了,返回304讓瀏覽器讀過(guò)期緩存。如果不相等就重新拉取。
Last-Modified
語(yǔ)法:Last-Modified: 星期,日期 月份 年份 時(shí):分:秒 GMT
Last-Modified:?Tue,?04?Apr?2017?10:01:15?GMT1
用于標(biāo)記請(qǐng)求資源的最后一次修改時(shí)間, 格式為GMT(格林尼治標(biāo)準(zhǔn)時(shí)間). 如可用new Date().toGMTString()獲取當(dāng)前GMT時(shí)間. Last-Modified ? ? ? ? ? ?是 ETag 的fallback機(jī)制, 優(yōu)先級(jí)比 ETag 低, 且只能精確到秒, 因此不太適合短時(shí)間內(nèi)頻繁改動(dòng)的資源. 不僅如此, 服務(wù)器端的靜態(tài)資源, 通常需要編譯打包, 可能出現(xiàn)資源內(nèi)容沒(méi)有改變, ? ? ? ? ? ?而Last-Modified卻改變的情況。
If-Modified-Since
語(yǔ)法同上, 如:
If-Modified-Since:?Tue,?04?Apr?2017?10:12:27?GMT1
緩存校驗(yàn)字段, 其值為上次響應(yīng)頭的Last-Modified值, 若與請(qǐng)求資源當(dāng)前的Last-Modified值相同, 那么將返回304狀態(tài)碼的響應(yīng), 反之, 將返回200狀態(tài)碼響應(yīng).
ETag
ETag:"fcb82312d92970bdf0d18a4eca08ebc7efede4fe"1
實(shí)體標(biāo)簽, 服務(wù)器資源的唯一標(biāo)識(shí)符, 瀏覽器可以根據(jù)ETag值緩存數(shù)據(jù), 節(jié)省帶寬. 如果資源已經(jīng)改變, etag可以幫助防止同步更新資源的相互覆蓋. ETag 優(yōu)先級(jí)比 Last-Modified 高.
If-Match
語(yǔ)法:?If-Match: ETag_value?或者?If-Match: ETag_value, ETag_value, …
緩存校驗(yàn)字段, 其值為上次收到的一個(gè)或多個(gè)etag?值. 常用于判斷條件是否滿足, 如下兩種場(chǎng)景:
對(duì)于 GET 或 HEAD 請(qǐng)求, 結(jié)合 Range 頭字段, 它可以保證新范圍的請(qǐng)求和前一個(gè)來(lái)自相同的源, 如果不匹配, 服務(wù)器將返回一個(gè)416(Range Not ? ? ? ? ? ? ? ? ? ?Satisfiable)狀態(tài)碼的響應(yīng).
對(duì)于 PUT 或者其他不安全的請(qǐng)求,?If-Match?可用于阻止錯(cuò)誤的更新操作, 如果不匹配, 服務(wù)器將返回一個(gè)412(Precondition ? ? ? ? ? ? ? ? ? ?Failed)狀態(tài)碼的響應(yīng).
If-None-Match
語(yǔ)法:?If-None-Match: ETag_value?或者?If-None-Match: ETag_value, ETag_value, …
緩存校驗(yàn)字段, 結(jié)合ETag字段, 常用于判斷緩存資源是否有效, 優(yōu)先級(jí)比If-Modified-Since高.
對(duì)于 GET 或 HEAD 請(qǐng)求, 如果其etags列表均不匹配, 服務(wù)器將返回200狀態(tài)碼的響應(yīng), 反之, 將返回304(Not Modified)狀態(tài)碼的響應(yīng). 無(wú)論是200還是304響應(yīng), ? ? ? ? ? ? ? ? ? ?都至少返回?Cache-Control,?Content-Location,?Date,ETag,?Expires, and?Vary?中之一的字段.
對(duì)于其他更新服務(wù)器資源的請(qǐng)求, 如果其etags列表匹配, 服務(wù)器將執(zhí)行更新, 反之, 將返回412(Precondition Failed)狀態(tài)碼的響應(yīng)。
If-Unmodified-Since
緩存校驗(yàn)字段, 語(yǔ)法同上. 表示資源未修改則正常執(zhí)行更新, 否則返回412(Precondition Failed)狀態(tài)碼的響應(yīng). 常用于如下兩種場(chǎng)景:
不安全的請(qǐng)求, 比如說(shuō)使用post請(qǐng)求更新wiki文檔, 文檔未修改時(shí)才執(zhí)行更新.
與 If-Range 字段同時(shí)使用時(shí), 可以用來(lái)保證新的片段請(qǐng)求來(lái)自一個(gè)未修改的文檔.
ETag&(If-Match&If-None-Match)關(guān)系如同Last-Modified&if-modified-since。
Etag VS. Last-Modified
Etag和Last-Modified都可以用于對(duì)資源進(jìn)行驗(yàn)證,而Last-Modified顧名思義,表示資源最后的更新時(shí)間。
我們把這兩者都成為驗(yàn)證器(Validators),不同的是,Etag屬于強(qiáng)驗(yàn)證(Strong Validation),因?yàn)樗谕氖琴Y源字節(jié)級(jí)別的一致;而Last-Modified屬于弱驗(yàn)證(Weak Validation),只要資源的主要內(nèi)容一致即可,允許例如頁(yè)底的廣告,頁(yè)腳不同。
根據(jù)RFC 2616標(biāo)準(zhǔn)中的13.3.4小節(jié),一個(gè)使用HTTP 1.1標(biāo)準(zhǔn)的服務(wù)端應(yīng)該(SHOULD)同時(shí)發(fā)送Etag和Last-Modified字段。同時(shí)一個(gè)支持HTTP 1.1的客戶端,比如瀏覽器,如果服務(wù)端有提供Etag的話,必須(MUST)首先對(duì)Etag進(jìn)行Conditional Request(If-None-Match頭信息);如果兩者都有提供,那么應(yīng)該(SHOULD)同時(shí)對(duì)兩者進(jìn)行Conditional Request(If-Modified-Since頭信息)。如果服務(wù)端對(duì)兩者的驗(yàn)證結(jié)果不一致,例如通過(guò)一個(gè)條件判斷資源發(fā)生了更改,而另一個(gè)判定資源沒(méi)有發(fā)生更改,則不允許返回304狀態(tài)。但話說(shuō)回來(lái),是否返回還是通過(guò)服務(wù)端編寫的實(shí)際代碼決定的。所以仍然有操縱的空間。
強(qiáng)緩存
一旦資源命中強(qiáng)緩存, 瀏覽器便不會(huì)向服務(wù)器發(fā)送請(qǐng)求, 而是直接讀取緩存. Chrome下的現(xiàn)象是?200 OK (from disk cache)?或者?200 OK (from ? ? ? ? ? ?memory cache). 如下:
對(duì)于常規(guī)請(qǐng)求, 只要存在該資源的緩存, 且Cache-Control:max-age 或者expires沒(méi)有過(guò)期, 那么就能命中強(qiáng)緩存.
協(xié)商緩存
緩存過(guò)期后, 繼續(xù)請(qǐng)求該資源, 對(duì)于現(xiàn)代瀏覽器, 擁有如下兩種做法:
根據(jù)上次響應(yīng)中的ETag_value, 自動(dòng)往request header中添加If-None-Match字段. 服務(wù)器收到請(qǐng)求后, ? ? ? ? ? ? ? ? ? ?拿If-None-Match字段的值與資源的ETag值進(jìn)行比較, 若相同, 則命中協(xié)商緩存, 返回304響應(yīng).
根據(jù)上次響應(yīng)中的Last-Modified_value, 自動(dòng)往request header中添加If-Modified-Since字段. 服務(wù)器收到請(qǐng)求后, 拿If-Modified-Since字段的值與資源的Last-Modified值進(jìn)行比較, ? ? ? ? ? ? ? ? ? ?若相同, 則命中協(xié)商緩存, 返回304響應(yīng).
以上, ETag優(yōu)先級(jí)比Last-Modified高, 同時(shí)存在時(shí), 前者覆蓋后者. 下面通過(guò)實(shí)例來(lái)理解下強(qiáng)緩存和協(xié)商緩存.
如下忽略首次訪問(wèn), 第二次通過(guò)?If-Modified-Since?命中了304協(xié)商緩存.
協(xié)商緩存的響應(yīng)結(jié)果, 不僅驗(yàn)證了資源的有效性, 同時(shí)還更新了瀏覽器緩存. 主要更新內(nèi)容如下:
Age:0
Cache-Control:max-age=600
Date:?Wed,?05?Apr?2017?13:09:36?GMTExpires:Wed,?05?Apr?2017?00:55:35?GMT1234
Age:0?表示命中了代理服務(wù)器的緩存, age值為0表示代理服務(wù)器剛剛刷新了一次緩存.
Cache-Control:max-age=600?覆蓋?Expires?字段, 表示從Date_value, 即?Wed, 05 Apr 2017 ? ? ? ? ? ?13:09:36 GMT?起, 10分鐘之后緩存過(guò)期. 因此10分鐘之內(nèi)訪問(wèn), 將會(huì)命中強(qiáng)緩存, 如下所示:
當(dāng)然, 除了上述與緩存直接相關(guān)的字段外, http header中還包括如下間接相關(guān)的字段.
Pragma
http1.0字段, 通常設(shè)置為Pragma:no-cache, 作用同Cache-Control:no-cache. ? ? ? ? ? ?當(dāng)一個(gè)no-cache請(qǐng)求發(fā)送給一個(gè)不遵循HTTP/1.1的服務(wù)器時(shí), 客戶端應(yīng)該包含pragma指令. 為此, 勾選?? 上disable cache時(shí), 瀏覽器自動(dòng)帶上了pragma字段. 如下:
Age
出現(xiàn)此字段, 表示命中代理服務(wù)器的緩存. 它指的是代理服務(wù)器對(duì)于請(qǐng)求資源的已緩存時(shí)間, 單位為秒. 如下:
Age:2383321
Date:Wed,?08?Mar?2017?16:12:42?GMT12
以上指的是, 代理服務(wù)器在2017年3月8日16:12:42時(shí)向源服務(wù)器發(fā)起了對(duì)該資源的請(qǐng)求, 目前已緩存了該資源2383321秒
Date
指的是響應(yīng)生成的時(shí)間. 請(qǐng)求經(jīng)過(guò)代理服務(wù)器時(shí), 返回的Date未必是最新的, 通常這個(gè)時(shí)候, 代理服務(wù)器將增加一個(gè)Age字段告知該資源已緩存了多久.
Vary
對(duì)于服務(wù)器而言, 資源文件可能不止一個(gè)版本, 比如說(shuō)壓縮和未壓縮, 針對(duì)不同的客戶端, 通常需要返回不同的資源版本. 比如說(shuō)老式的瀏覽器可能不支持解壓縮, 這個(gè)時(shí)候, 就需要返回一個(gè)未壓縮的版本; 對(duì)于新的瀏覽器, ? ? ? ? ? ?支持壓縮, 返回一個(gè)壓縮的版本, 有利于節(jié)省帶寬, 提升體驗(yàn). 那么怎么區(qū)分這個(gè)版本呢, 這個(gè)時(shí)候就需要Vary了.
服務(wù)器通過(guò)指定Vary: Accept-Encoding, 告知代理服務(wù)器, 對(duì)于這個(gè)資源, 需要緩存兩個(gè)版本: 壓縮和未壓縮. 這樣老式瀏覽器和新的瀏覽器, 通過(guò)代理, ? ? ? ? ? ?就分別拿到了未壓縮和壓縮版本的資源, 避免了都拿同一個(gè)資源的尷尬.
Vary:Accept-Encoding,User-Agent1
如上設(shè)置, 代理服務(wù)器將針對(duì)是否壓縮和瀏覽器類型兩個(gè)維度去緩存資源. 如此一來(lái), 同一個(gè)url, 就能針對(duì)PC和Mobile返回不同的緩存內(nèi)容。
怎么讓瀏覽器不緩存靜態(tài)資源
實(shí)際上, 工作中很多場(chǎng)景都需要避免瀏覽器緩存, 除了瀏覽器隱私模式, 請(qǐng)求時(shí)想要禁用緩存, 還可以設(shè)置請(qǐng)求頭:?Cache-Control: no-cache, no-store, must-revalidate?.
當(dāng)然, 還有一種常用做法: 即給請(qǐng)求的資源增加一個(gè)版本號(hào), 如下:
這樣做的好處就是你可以自由控制什么時(shí)候加載最新的資源.
不僅如此, HTML也可以禁用緩存, 即在頁(yè)面的meta設(shè)置
上述雖能禁用緩存, 但只有部分瀏覽器支持, 而且由于代理不解析HTML文檔, 故代理服務(wù)器也不支持這種方式.
IE8的異常表現(xiàn)
實(shí)際上, 上述緩存有關(guān)的規(guī)律, 并非所有瀏覽器都完全遵循. 比如說(shuō)IE8.
資源緩存是否有效相關(guān).
瀏覽器前提操作表現(xiàn)正常表現(xiàn)
IE8資源緩存有效新開一個(gè)窗口加載網(wǎng)頁(yè)重新發(fā)送請(qǐng)求(返回200)展示緩存的頁(yè)面
IE8資源緩存失效原瀏覽器窗口中單擊 Enter 按鈕展示緩存的頁(yè)面重新發(fā)送請(qǐng)求(返回200)
Last-Modified / E-Tag 相關(guān).
瀏覽器前提操作表現(xiàn)正常表現(xiàn)
IE8資源內(nèi)容沒(méi)有修改新開一個(gè)窗口加載網(wǎng)頁(yè)瀏覽器重新發(fā)送請(qǐng)求(返回200)重新發(fā)送請(qǐng)求(返回304)
IE8資源內(nèi)容已修改原瀏覽器窗口中單擊 Enter 按鈕瀏覽器展示緩存的頁(yè)面重新發(fā)送請(qǐng)求(返回200)
本文改編自louis的《瀏覽器緩存機(jī)制剖析》
文章來(lái)源:瀏覽器緩存機(jī)制剖析 - Standard Generalized Markup langu - 周陸軍的個(gè)人網(wǎng)站
此文如有不妥之處,請(qǐng)告知,謝謝!
參考文章
Cache Policy Interaction—Maximum Age and Maximum Staleness
HTTP/1.1: Header ? ? ? ? ? ? ? ? ? ? ? ?Field Definitions
Cache-Control ? ? ? ? ? ? ? ? ? ? ? ?- HTTP | MDN
徹底弄懂 Http 緩存機(jī)制 基于緩存策略三要素分解法
設(shè)計(jì)一個(gè)無(wú)懈可擊的瀏覽器緩存方案:關(guān)于思路,細(xì)節(jié),ServiceWorker,以及HTTP/2