原文:VaJoy Larn(@VaJoy_學(xué)霸模式重啟)
www.cnblogs.com/vajoy/p/5341664.html
針對(duì)瀏覽器的http緩存的分析也算是老生常談了,每隔一段時(shí)間就會(huì)冒出一篇不錯(cuò)的文章,其原理也是各大公司面試時(shí)幾乎必考的問題。
之所以還寫一篇這樣的文章惯豆,是因?yàn)榻诙荚诟阈录夹g(shù)炉擅,想“回歸”下基礎(chǔ)灿椅,也希望盡量總結(jié)的更詳盡些。
那么你是否還需要閱讀本篇文章呢正蛙?可以試著回答下面這個(gè)問題:
我們?cè)谠L問百度首頁的時(shí)候抚太,會(huì)發(fā)現(xiàn)不管怎么刷新頁面塘幅,靜態(tài)資源基本都是返回 200(from cache):
隨便點(diǎn)開一個(gè)靜態(tài)資源是醬的:
哎喲有Response報(bào)頭數(shù)據(jù)呢菇怀,看來服務(wù)器也正常返回了etag什么鬼的應(yīng)有盡有,那狀態(tài)200不是應(yīng)該對(duì)應(yīng)的非緩存狀態(tài)么晌块?要from cache的話不是應(yīng)該返回304才合理么?
難道是度娘的服務(wù)器故障了嗎帅霜?
如果你知道答案匆背,那就可以忽略本文了。
http報(bào)文中與緩存相關(guān)的首部字段
我們先來瞅一眼RFC2616規(guī)定的47種http報(bào)文首部字段中與緩存相關(guān)的字段身冀,事先了解一下能讓咱在心里有個(gè)底:
1. 通用首部字段(就是請(qǐng)求報(bào)文和響應(yīng)報(bào)文都能用上的字段)
2. 請(qǐng)求首部字段
3. 響應(yīng)首部字段
4. 實(shí)體首部字段
后續(xù)大體也會(huì)依次介紹它們钝尸。
場(chǎng)景模擬
為方便模擬各種緩存效果,我們建個(gè)非常簡(jiǎn)單的場(chǎng)景搂根。
1. 頁面文件
我們建個(gè)非常簡(jiǎn)單的html頁面珍促,上面只有一個(gè)本地樣式文件和圖片:
<!DOCTYPE html>
<html>
<head>
<title>緩存測(cè)試</title>
<link?rel="stylesheet"?href="css/reset.css">
</head>
<body>
<h1>哥只是一個(gè)標(biāo)題</h1>
<p><img?src="img/dog.jpg"?/></p>
</body>
</html>
2. 首部字段修改
有時(shí)候一些瀏覽器會(huì)自行給請(qǐng)求首部加上一些字段(如chrome使用F5會(huì)強(qiáng)制加上“cache-control:max-age=0”),會(huì)覆蓋掉一些字段(比如pragma)的功能;另外有時(shí)候我們希望服務(wù)器能多/少返回一些響應(yīng)字段剩愧。
這種情況我們就希望可以手動(dòng)來修改請(qǐng)求或響應(yīng)報(bào)文上的內(nèi)容了猪叙。那么如何實(shí)現(xiàn)呢?這里我們使用Fiddler來完成任務(wù)仁卷。
在Fiddler中我們可以通過“bpu XXX”指令來攔截指定請(qǐng)求穴翩,然后手動(dòng)修改請(qǐng)求內(nèi)容再發(fā)給服務(wù)器、修改響應(yīng)內(nèi)容再發(fā)給客戶端锦积。
以我們的example為例芒帕,頁面文件走nginx通過 http://localhost/ 可直接訪問,所以我們直接執(zhí)行“bpu localhost”攔截所有地址中帶有該字樣的請(qǐng)求:
點(diǎn)擊被攔截的請(qǐng)求丰介,可以在右欄直接修改報(bào)文內(nèi)容(上半?yún)^(qū)域是請(qǐng)求報(bào)文背蟆,下半?yún)^(qū)域是響應(yīng)報(bào)文),點(diǎn)擊黃色的“Break on Response”按鈕可以執(zhí)行下一步(把請(qǐng)求發(fā)給服務(wù)器)哮幢,點(diǎn)擊綠色的按鈕“Run to Completion”可以直接完成整個(gè)請(qǐng)求過程:
通過這個(gè)方法我們可以很輕松地模擬出各種http緩存場(chǎng)景带膀。
3. 瀏覽器的強(qiáng)制策略
如上述,當(dāng)下大多數(shù)瀏覽器在點(diǎn)擊刷新按鈕或按F5時(shí)會(huì)自行加上“Cache-Control:max-age=0”請(qǐng)求字段橙垢,所以我們先約定成俗——后文提及的“刷新”多指的是選中url地址欄并按回車鍵(這樣不會(huì)被強(qiáng)行加上Cache-Control)本砰。
事實(shí)上有的瀏覽器還有一些更奇怪的行為,在后續(xù)我們回答文章開頭問題的時(shí)候會(huì)提到钢悲。
石器時(shí)代的緩存方式
在 http1.0 時(shí)代点额,給客戶端設(shè)定緩存方式可通過兩個(gè)字段——“Pragma”和“Expires”來規(guī)范。雖然這兩個(gè)字段早可拋棄莺琳,但為了做http協(xié)議的向下兼容还棱,你還是可以看到很多網(wǎng)站依舊會(huì)帶上這兩個(gè)字段。
1. Pragma
當(dāng)該字段值為“no-cache”的時(shí)候(事實(shí)上現(xiàn)在RFC中也僅標(biāo)明該可選值)惭等,會(huì)知會(huì)客戶端不要對(duì)該資源讀緩存珍手,即每次都得向服務(wù)器發(fā)一次請(qǐng)求才行。
Pragma屬于通用首部字段,在客戶端上使用時(shí)琳要,常規(guī)要求我們往html上加上這段meta元標(biāo)簽(而且可能還得做些hack放到body后面去):
<meta http-equiv="Pragma" content="no-cache">
它告訴瀏覽器每次請(qǐng)求頁面時(shí)都不要讀緩存寡具,都得往服務(wù)器發(fā)一次請(qǐng)求才行。
BUT!!! 事實(shí)上這種禁用緩存的形式用處很有限:
1. 僅有IE才能識(shí)別這段meta標(biāo)簽含義稚补,其它主流瀏覽器僅能識(shí)別“Cache-Control: no-store”的meta標(biāo)簽(見出處)童叠。
2. 在IE中識(shí)別到該meta標(biāo)簽含義,并不一定會(huì)在請(qǐng)求字段加上Pragma课幕,但的確會(huì)讓當(dāng)前頁面每次都發(fā)新請(qǐng)求(僅限頁面厦坛,頁面上的資源則不受影響)。
做了測(cè)試后發(fā)現(xiàn)也的確如此乍惊,這種客戶端定義Pragma的形式基本沒起到多少作用杜秸。
不過如果是在響應(yīng)報(bào)文上加上該字段就不一樣了:
如上圖紅框部分是再次刷新頁面時(shí)生成的請(qǐng)求,這說明禁用緩存生效润绎,預(yù)計(jì)瀏覽器在收到服務(wù)器的Pragma字段后會(huì)對(duì)資源進(jìn)行標(biāo)記撬碟,禁用其緩存行為,進(jìn)而后續(xù)每次刷新頁面均能重新發(fā)出請(qǐng)求而不走緩存莉撇。
2. Expires
有了Pragma來禁用緩存小作,自然也需要有個(gè)東西來啟用緩存和定義緩存時(shí)間,對(duì)http1.0而言稼钩,Expires就是做這件事的首部字段顾稀。
Expires的值對(duì)應(yīng)一個(gè)GMT(格林尼治時(shí)間),比如“Mon, 22 Jul 2002 11:12:01 GMT”來告訴瀏覽器資源緩存過期時(shí)間坝撑,如果還沒過該時(shí)間點(diǎn)則不發(fā)請(qǐng)求静秆。
在客戶端我們同樣可以使用meta標(biāo)簽來知會(huì)IE(也僅有IE能識(shí)別)頁面(同樣也只對(duì)頁面有效,對(duì)頁面上的資源無效)緩存時(shí)間:
<meta http-equiv="expires" content="mon, 18 apr 2016 14:30:00 GMT">
如果希望在IE下頁面不走緩存巡李,希望每次刷新頁面都能發(fā)新請(qǐng)求抚笔,那么可以把“content”里的值寫為“-1”或“0”。
注意的是該方式僅僅作為知會(huì)IE緩存時(shí)間的標(biāo)記侨拦,你并不能在請(qǐng)求或響應(yīng)報(bào)文中找到Expires字段殊橙。
如果是在服務(wù)端報(bào)頭返回Expires字段,則在任何瀏覽器中都能正確設(shè)置資源緩存的時(shí)間:
在上圖里狱从,緩存時(shí)間設(shè)置為一個(gè)已過期的時(shí)間點(diǎn)(見紅框)膨蛮,則刷新頁面將重新發(fā)送請(qǐng)求(見藍(lán)框)。
那么如果Pragma和Expires一起上陣的話季研,聽誰的敞葛?我們?cè)囈辉嚲椭懒耍?/p>
我們通過Pragma禁用緩存,又給Expires定義一個(gè)還未到期的時(shí)間(紅框)与涡,刷新頁面時(shí)發(fā)現(xiàn)均發(fā)起了新請(qǐng)求(藍(lán)框)惹谐,這意味著Pragma字段的優(yōu)先級(jí)會(huì)更高持偏。
BUT,響應(yīng)報(bào)文中Expires所定義的緩存時(shí)間是相對(duì)服務(wù)器上的時(shí)間而言的氨肌,如果客戶端上的時(shí)間跟服務(wù)器上的時(shí)間不一致(特別是用戶修改了自己電腦的系統(tǒng)時(shí)間)鸿秆,那緩存時(shí)間可能就沒啥意義了。
Cache-Control
針對(duì)上述的“Expires時(shí)間是相對(duì)服務(wù)器而言怎囚,無法保證和客戶端時(shí)間統(tǒng)一”的問題卿叽,http1.1新增了 Cache-Control 來定義緩存過期時(shí)間,若報(bào)文中同時(shí)出現(xiàn)了 Pragma桩了、Expires 和 Cache-Control,會(huì)以 Cache-Control 為準(zhǔn)埠戳。
Cache-Control也是一個(gè)通用首部字段井誉,這意味著它能分別在請(qǐng)求報(bào)文和響應(yīng)報(bào)文中使用。在RFC中規(guī)范了 Cache-Control 的格式為:
"Cache-Control" ":" cache-directive
作為請(qǐng)求首部時(shí)整胃,cache-directive 的可選值有:
作為響應(yīng)首部時(shí)颗圣,cache-directive 的可選值有:
我們依舊可以在HTML頁面加上meta標(biāo)簽來給請(qǐng)求報(bào)頭加上 Cache-Control 字段:
另外 Cache-Control 允許自由組合可選值,例如:
Cache-Control: max-age=3600, must-revalidate
它意味著該資源是從原服務(wù)器上取得的屁使,且其緩存(新鮮度)的有效時(shí)間為一小時(shí)在岂,在后續(xù)一小時(shí)內(nèi),用戶重新訪問該資源則無須發(fā)送請(qǐng)求蛮寂。
當(dāng)然這種組合的方式也會(huì)有些限制蔽午,比如 no-cache 就不能和 max-age、min-fresh酬蹋、max-stale 一起搭配使用及老。
組合的形式還能做一些瀏覽器行為不一致的兼容處理。例如在IE我們可以使用 no-cache 來防止點(diǎn)擊“后退”按鈕時(shí)頁面資源從緩存加載范抓,但在 Firefox 中骄恶,需要使用 no-store 才能防止歷史回退時(shí)瀏覽器不從緩存中去讀取數(shù)據(jù),故我們?cè)陧憫?yīng)報(bào)頭加上如下組合值即可做兼容處理:
Cache-Control: no-cache, no-store
緩存校驗(yàn)字段
上述的首部字段均能讓客戶端決定是否向服務(wù)器發(fā)送請(qǐng)求匕垫,比如設(shè)置的緩存時(shí)間未過期僧鲁,那么自然直接從本地緩存取數(shù)據(jù)即可(在chrome下表現(xiàn)為200 from cache),若緩存時(shí)間過期了或資源不該直接走緩存象泵,則會(huì)發(fā)請(qǐng)求到服務(wù)器去寞秃。
我們現(xiàn)在要說的問題是,如果客戶端向服務(wù)器發(fā)了請(qǐng)求偶惠,那么是否意味著一定要讀取回該資源的整個(gè)實(shí)體內(nèi)容呢蜕该?
我們?cè)囍@么想——客戶端上某個(gè)資源保存的緩存時(shí)間過期了氧枣,但這時(shí)候其實(shí)服務(wù)器并沒有更新過這個(gè)資源脖卖,如果這個(gè)資源數(shù)據(jù)量很大弟晚,客戶端要求服務(wù)器再把這個(gè)東西重新發(fā)一遍過來,是否非常浪費(fèi)帶寬和時(shí)間呢烦味?
答案是肯定的,那么是否有辦法讓服務(wù)器知道客戶端現(xiàn)在存有的緩存文件待侵,其實(shí)跟自己所有的文件是一致的胶滋,然后直接告訴客戶端說“這東西你直接用緩存里的就可以了,我這邊沒更新過呢皆的,就不再傳一次過去了”覆履。
為了讓客戶端與服務(wù)器之間能實(shí)現(xiàn)緩存文件是否更新的驗(yàn)證、提升緩存的復(fù)用率费薄,Http1.1新增了幾個(gè)首部字段來做這件事情硝全。
1. Last-Modified
服務(wù)器將資源傳遞給客戶端時(shí),會(huì)將資源最后更改的時(shí)間以“Last-Modified: GMT”的形式加在實(shí)體首部上一起返回給客戶端楞抡。
客戶端會(huì)為資源標(biāo)記上該信息伟众,下次再次請(qǐng)求時(shí),會(huì)把該信息附帶在請(qǐng)求報(bào)文中一并帶給服務(wù)器去做檢查召廷,若傳遞的時(shí)間值與服務(wù)器上該資源最終修改時(shí)間是一致的凳厢,則說明該資源沒有被修改過,直接返回304狀態(tài)碼即可竞慢。
至于傳遞標(biāo)記起來的最終修改時(shí)間的請(qǐng)求報(bào)文首部字段一共有兩個(gè):
⑴ If-Modified-Since: Last-Modified-value
示例為 ?If-Modified-Since: Thu, 31 Mar 2016 07:07:52 GMT
該請(qǐng)求首部告訴服務(wù)器如果客戶端傳來的最后修改時(shí)間與服務(wù)器上的一致先紫,則直接回送304 和響應(yīng)報(bào)頭即可。
當(dāng)前各瀏覽器均是使用的該請(qǐng)求首部來向服務(wù)器傳遞保存的 Last-Modified 值筹煮。
⑵ If-Unmodified-Since: Last-Modified-value
告訴服務(wù)器遮精,若Last-Modified沒有匹配上(資源在服務(wù)端的最后更新時(shí)間改變了),則應(yīng)當(dāng)返回412(Precondition Failed) 狀態(tài)碼給客戶端败潦。
當(dāng)遇到下面情況時(shí)仑鸥,If-Unmodified-Since 字段會(huì)被忽略:
1. Last-Modified值對(duì)上了(資源在服務(wù)端沒有新的修改);
2. 服務(wù)端需返回2XX和412之外的狀態(tài)碼变屁;
3. 傳來的指定日期不合法
Last-Modified 說好卻也不是特別好眼俊,因?yàn)槿绻诜?wù)器上,一個(gè)資源被修改了粟关,但其實(shí)際內(nèi)容根本沒發(fā)生改變疮胖,會(huì)因?yàn)長ast-Modified時(shí)間匹配不上而返回了整個(gè)實(shí)體給客戶端(即使客戶端緩存里有個(gè)一模一樣的資源)。
2. ETag
為了解決上述Last-Modified可能存在的不準(zhǔn)確的問題闷板,Http1.1還推出了 ETag 實(shí)體首部字段澎灸。
服務(wù)器會(huì)通過某種算法,給資源計(jì)算得出一個(gè)唯一標(biāo)志符(比如md5標(biāo)志)遮晚,在把資源響應(yīng)給客戶端的時(shí)候性昭,會(huì)在實(shí)體首部加上“ETag: 唯一標(biāo)識(shí)符”一起返回給客戶端。
客戶端會(huì)保留該 ETag 字段县遣,并在下一次請(qǐng)求時(shí)將其一并帶過去給服務(wù)器糜颠。服務(wù)器只需要比較客戶端傳來的ETag跟自己服務(wù)器上該資源的ETag是否一致汹族,就能很好地判斷資源相對(duì)客戶端而言是否被修改過了。
如果服務(wù)器發(fā)現(xiàn)ETag匹配不上其兴,那么直接以常規(guī)GET 200回包形式將新的資源(當(dāng)然也包括了新的ETag)發(fā)給客戶端顶瞒;如果ETag是一致的,則直接返回304知會(huì)客戶端直接使用本地緩存即可元旬。
那么客戶端是如何把標(biāo)記在資源上的 ETag 傳去給服務(wù)器的呢榴徐?請(qǐng)求報(bào)文中有兩個(gè)首部字段可以帶上 ETag 值:
⑴ If-None-Match: ETag-value
示例為 ?If-None-Match: "56fcccc8-1699"
告訴服務(wù)端如果 ETag 沒匹配上需要重發(fā)資源數(shù)據(jù),否則直接回送304 和響應(yīng)報(bào)頭即可匀归。
當(dāng)前各瀏覽器均是使用的該請(qǐng)求首部來向服務(wù)器傳遞保存的 ETag 值坑资。
⑵ If-Match: ETag-value
告訴服務(wù)器如果沒有匹配到ETag,或者收到了“*”值而當(dāng)前并沒有該資源實(shí)體穆端,則應(yīng)當(dāng)返回412(Precondition Failed) 狀態(tài)碼給客戶端袱贮。否則服務(wù)器直接忽略該字段。
If-Match 的一個(gè)應(yīng)用場(chǎng)景是徙赢,客戶端走PUT方法向服務(wù)端請(qǐng)求上傳/更替資源字柠,這時(shí)候可以通過 If-Match 傳遞資源的ETag探越。
需要注意的是狡赐,如果資源是走分布式服務(wù)器(比如CDN)存儲(chǔ)的情況,需要這些服務(wù)器上計(jì)算ETag唯一值的算法保持一致钦幔,才不會(huì)導(dǎo)致明明同一個(gè)文件枕屉,在服務(wù)器A和服務(wù)器B上生成的ETag卻不一樣。
如果 Last-Modified 和 ETag 同時(shí)被使用鲤氢,則要求它們的驗(yàn)證都必須通過才會(huì)返回304搀擂,若其中某個(gè)驗(yàn)證沒通過,則服務(wù)器會(huì)按常規(guī)返回資源實(shí)體及200狀態(tài)碼卷玉。
在較新的 nginx 上默認(rèn)是同時(shí)開啟了這兩個(gè)功能的:
上圖的前三條請(qǐng)求是原始請(qǐng)求哨颂,接著的三條請(qǐng)求是刷新頁面后的新請(qǐng)求,在發(fā)新請(qǐng)求之前我們修改了 reset.css 文件相种,所以它的 Last-Modified 和 ETag 均發(fā)生了改變威恼,服務(wù)器因此返回了新的文件給客戶端(狀態(tài)值為200)。
而 dog.jpg 我們沒有做修改寝并,其Last-Modified 和 ETag在服務(wù)端是保持不變的箫措,故服務(wù)器直接返回了304狀態(tài)碼讓客戶端直接使用緩存的 dog.jpg 即可,沒有把實(shí)體內(nèi)容返回給客戶端(因?yàn)闆]必要)衬潦。
緩存實(shí)踐
當(dāng)我們?cè)谝粋€(gè)項(xiàng)目上做http緩存的應(yīng)用時(shí)斤蔓,我們還是會(huì)把上述提及的大多數(shù)首部字段均使用上,例如使用 Expires 來兼容舊的瀏覽器镀岛,使用 Cache-Control 來更精準(zhǔn)地利用緩存弦牡,然后開啟 ETag 跟 Last-Modified 功能進(jìn)一步復(fù)用緩存減少流量友驮。
那么這里會(huì)有一個(gè)小問題——Expires 和 Cache-Control 的值應(yīng)設(shè)置為多少合適呢?
答案是不會(huì)有過于精準(zhǔn)的值喇伯,均需要進(jìn)行按需評(píng)估喊儡。
例如頁面鏈接的請(qǐng)求常規(guī)是無須做長時(shí)間緩存的,從而保證回退到頁面時(shí)能重新發(fā)出請(qǐng)求稻据,百度首頁是用的 Cache-Control:private艾猜,騰訊首頁則是設(shè)定了60秒的緩存,即 Cache-Control:max-age=60捻悯。
而靜態(tài)資源部分匆赃,特別是圖片資源,通常會(huì)設(shè)定一個(gè)較長的緩存時(shí)間今缚,而且這個(gè)時(shí)間最好是可以在客戶端靈活修改的算柳。以騰訊的某張圖片為例:
http://i.gtimg.cn/vipstyle/vipportal/v4/img/common/logo.png?max_age=2592000
客戶端可以通過給圖片加上“max_age”的參數(shù)來定義服務(wù)器返回的緩存時(shí)間:
當(dāng)然這需要有一個(gè)前提——靜態(tài)資源能確保長時(shí)間不做改動(dòng)。如果一個(gè)腳本文件響應(yīng)給客戶端并做了長時(shí)間的緩存姓言,而服務(wù)端在近期修改了該文件的話瞬项,緩存了此腳本的客戶端將無法及時(shí)獲得新的數(shù)據(jù)。
解決該困擾的辦法也簡(jiǎn)單——把服務(wù)側(cè)ETag的那一套也搬到前端來用——頁面的靜態(tài)資源以版本形式發(fā)布何荚,常用的方法是在文件名或參數(shù)帶上一串md5或時(shí)間標(biāo)記符:
https://#/hm.js?e23800c454aa573c0ccb16b52665ac26
http://tb1.bdstatic.com/tb/_/tbean_safe_ajax_94e7ca2.js
http://img1.gtimg.com/ninja/2/2016/04/ninja145972803357449.jpg
如果文件被修改了囱淋,才更改其標(biāo)記符內(nèi)容,這樣能確辈吞粒客戶端能及時(shí)從服務(wù)器收取到新修改的文件妥衣。
關(guān)于開頭的問題
現(xiàn)在回過頭來看文章開頭的問題,可能會(huì)覺得答案很容易回答出來戒傻。
百度首頁的資源在刷新后實(shí)際沒有發(fā)送任何請(qǐng)求税手,因?yàn)?Cache-Control 定義的緩存時(shí)間段還沒到期。在Chrome中即使沒發(fā)送請(qǐng)求需纳,但只要從本地的緩存中取芦倒,都會(huì)在Network面板顯示一條狀態(tài)為200且注明“from cache”的偽請(qǐng)求,其Response內(nèi)容只是上一次回包留下的數(shù)據(jù)不翩。
然而這并不是問題的全部答案兵扬,我們前面提到過,在Chrome中如果點(diǎn)擊“刷新”按鈕慌盯,Chrome會(huì)強(qiáng)制給所有資源加上“Cache-Control: max-age=0”的請(qǐng)求首部并向服務(wù)器發(fā)送驗(yàn)證請(qǐng)求的周霉,而在文章開頭的動(dòng)圖中,我們的確點(diǎn)擊了“刷新”按鈕亚皂,卻不見瀏覽器發(fā)去新請(qǐng)求(并返回304)俱箱。
關(guān)于這個(gè)問題其實(shí)在組內(nèi)跟小伙伴們討論過,通過Fiddler抓包發(fā)現(xiàn)灭必,如果關(guān)閉Chrome的開發(fā)者面板再點(diǎn)擊“刷新”按鈕狞谱,瀏覽器是會(huì)按預(yù)期發(fā)送驗(yàn)證請(qǐng)求且接收返回的304響應(yīng)的乃摹,另外這個(gè)奇怪的情況在不同的網(wǎng)站甚至不同的電腦下出現(xiàn)頻率都不一致,所以暫時(shí)將其歸咎于瀏覽器的怪異反應(yīng)跟衅。
那么有這么一個(gè)問題——是否有辦法在瀏覽器點(diǎn)擊“刷新”按鈕的時(shí)候不讓瀏覽器去發(fā)新的驗(yàn)證請(qǐng)求呢孵睬?
辦法還是有的,就是不怎么實(shí)用——在頁面加載完畢后通過腳本動(dòng)態(tài)地添加資源:
$(window).load(function()?{
??????var?bg='http://img.infinitynewtab.com/wallpaper/100.jpg';
??????setTimeout(function()?{??//setTimeout是必須的
???????$('#bgOut').css('background-image',?'url('+bg+')');
??????},0);
});
出處來自知乎伶跷,更具體的解釋可以去看看掰读。
其它相關(guān)的首部字段
事實(shí)上較常用和重要的緩存相關(guān)字段我們都介紹完了,這里順帶講講幾個(gè)跟緩存有關(guān)系叭莫,但沒那么主要的響應(yīng)首部字段蹈集。
1. Vary
“vary”本身是“變化”的意思,而在http報(bào)文中更趨于是“vary from”(與雇初。拢肆。。不同)的含義靖诗,它表示服務(wù)端會(huì)以什么基準(zhǔn)字段來區(qū)分郭怪、篩選緩存版本。
我們先考慮這么一個(gè)問題——在服務(wù)端有著這么一個(gè)地址刊橘,如果是IE用戶則返回針對(duì)IE開發(fā)的內(nèi)容鄙才,否則返回另一個(gè)主流瀏覽器版本的內(nèi)容。這很簡(jiǎn)單伤为,服務(wù)端獲取到請(qǐng)求的 User-Agent 字段做處理即可咒循。但是用戶請(qǐng)求的是代理服務(wù)器而非原服務(wù)器据途,且代理服務(wù)器如果直接把緩存的IE版本資源發(fā)給了非IE的客戶端绞愚,這就出問題了。
因此 Vary 便是著手處理該問題的首部字段颖医,我們可以在響應(yīng)報(bào)文加上:
Vary: User-Agent
便能知會(huì)代理服務(wù)器需要以 User-Agent 這個(gè)請(qǐng)求首部字段來區(qū)別緩存版本位衩,防止傳遞給客戶端的緩存不正確。
Vary 也接受條件組合的形式:
Vary: User-Agent, Accept-Encoding
這意味著服務(wù)器應(yīng)以 User-Agent 和 Accept-Encoding 兩個(gè)請(qǐng)求首部字段來區(qū)分緩存版本熔萧。
2. Date 和 Age
HTTP并沒有提供某種方法來幫用戶區(qū)分其收到的資源是否命中了代理服務(wù)器的緩存糖驴,但在客戶端我們可以通過計(jì)算響應(yīng)報(bào)文中的 Date 和 Age 字段來得到答案。
Date 理所當(dāng)然是原服務(wù)器發(fā)送該資源響應(yīng)報(bào)文的時(shí)間(GMT格式)佛致,如果你發(fā)現(xiàn) Date 的時(shí)間與“當(dāng)前時(shí)間”差別較大贮缕,或者連續(xù)F5刷新發(fā)現(xiàn) Date 的值都沒變化,則說明你當(dāng)前請(qǐng)求是命中了代理服務(wù)器的緩存俺榆。
上述的“當(dāng)前時(shí)間”自然是相對(duì)于原服務(wù)器而言的時(shí)間感昼,那么如何獲悉原服務(wù)器的當(dāng)前時(shí)間呢?
常規(guī)從頁面地址請(qǐng)求的響應(yīng)報(bào)文中可獲得罐脊,以博客園首頁為例:
每次你刷新頁面定嗓,瀏覽器都會(huì)重新發(fā)出這條url的請(qǐng)求蜕琴,你會(huì)發(fā)現(xiàn)其 Date 值是不斷變化的,這說明該鏈接沒有命中緩存宵溅,都是從原服務(wù)器返回過來的數(shù)據(jù)凌简。
因此我們可以拿頁面上其它靜態(tài)資源請(qǐng)求回包中的 Date 與其進(jìn)行對(duì)比,若靜態(tài)資源的 Date 早于原服務(wù)端時(shí)間恃逻,則說明命中了代理服務(wù)器緩存雏搂。
通常還滿足這么個(gè)條件:
靜態(tài)資源Age + 靜態(tài)資源Date = 原服務(wù)端Date
這里的 Age 也是響應(yīng)報(bào)文中的首部字段,它表示該文件在代理服務(wù)器中存在的時(shí)間(秒)寇损,如文件被修改或替換畔派,Age會(huì)重新由0開始累計(jì)。
我們?cè)谏厦婺菑埐┛蛨@首頁報(bào)文截圖的同個(gè)場(chǎng)景下润绵,看看某個(gè)文件(jQuery.js)命中代理服務(wù)器緩存的回包數(shù)據(jù):
會(huì)發(fā)現(xiàn)它滿足我們上述的規(guī)則:
//return true
new?Date('Mon, 04 Apr 2016 07:03:17 GMT')/1000?==?new?Date('Sat, 19 Dec 2015 01:29:14 GMT')/1000?+?9264843
不過這條規(guī)則也不一定準(zhǔn)確线椰,特別是當(dāng)原服務(wù)器經(jīng)常修改系統(tǒng)時(shí)間的情況下。
關(guān)于http緩存原理的知識(shí)就整理到這尘盼,希望能讓你有所收獲憨愉,共勉~
感興趣的小伙伴,可以關(guān)注公眾號(hào)【grain先森】卿捎,回復(fù)關(guān)鍵詞 “vue”配紫,獲取更多資料,更多關(guān)鍵詞玩法期待你的探索~