瀏覽器的強緩存和協(xié)商緩存
這里說的緩存是指瀏覽器(客戶端)在本地磁盤中對訪問過的資源保存的副本文件疹尾。
瀏覽器緩存主要有以下幾個優(yōu)點:
- 減少重復(fù)數(shù)據(jù)請求液南,避免通過網(wǎng)絡(luò)再次加載資源肉瓦,節(jié)省流量。
- 降低服務(wù)器的壓力,提升網(wǎng)站性能岗照。
- 加快客戶端加載網(wǎng)頁的速度摆尝, 提升用戶體驗。
瀏覽器緩存分為強緩存和協(xié)商緩存逻恐,兩者有兩個比較明顯的區(qū)別:
- 如果瀏覽器命中強緩存像吻,則不需要給服務(wù)器發(fā)請求峻黍;而協(xié)商緩存最終由服務(wù)器來決定是否使用緩存,即客戶端與服務(wù)器之間存在一次通信拨匆。
- 在
chrome
中強緩存(雖然沒有發(fā)出真實的http
請求)的請求狀態(tài)碼返回是200 (from cache)
姆涩;而協(xié)商緩存如果命中走緩存的話,請求的狀態(tài)碼是304 (not modified)
惭每。 不同瀏覽器的策略不同骨饿,在Fire Fox
中,from cache
狀態(tài)碼是 304.
其中 from cache 會分為 from disk cache 和 from memory cache. 從內(nèi)存中獲取最快台腥,但是是 session 級別的緩存宏赘,關(guān)閉瀏覽器之后就沒有了。
[圖片上傳失敗...(image-ce437-1580376444419)]
請求流程
瀏覽器在第一次請求后緩存資源黎侈,再次請求時察署,會進(jìn)行下面兩個步驟:
- 瀏覽器會獲取該緩存資源的
header
中的信息,根據(jù)response header
中的expires
和cache-control
來判斷是否命中強緩存峻汉,如果命中則直接從緩存中獲取資源贴汪。 - 如果沒有命中強緩存,瀏覽器就會發(fā)送請求到服務(wù)器俱济,這次請求會帶上
IF-Modified-Since
或者IF-None-Match
, 它們的值分別是第一次請求返回Last-Modified
或者Etag
嘶是,由服務(wù)器來對比這一對字段來判斷是否命中。如果命中蛛碌,則服務(wù)器返回 304 狀態(tài)碼聂喇,并且不會返回資源內(nèi)容,瀏覽器會直接從緩存獲任敌希太;否則服務(wù)器最終會返回資源的實際內(nèi)容,并更新 header 中的相關(guān)緩存字段酝蜒。
借用網(wǎng)上的一張圖片
[圖片上傳失敗...(image-8a99bd-1580376444420)]
強緩存
強緩存是根據(jù)返回頭中的 Expires
或者 Cache-Control
兩個字段來控制的誊辉,都是表示資源的緩存有效時間。
-
Expires
是http 1.0
的規(guī)范亡脑,值是一個GMT
格式的時間點字符串堕澄,比如Expires:Mon,18 Oct 2066 23:59:59 GMT
。這個時間點代表資源失效的時間霉咨,如果當(dāng)前的時間戳在這個時間之前蛙紫,則判定命中緩存。有一個缺點是途戒,失效時間是一個絕對時間坑傅,如果服務(wù)器時間與客戶端時間偏差較大時,就會導(dǎo)致緩存混亂喷斋。而服務(wù)器的時間跟用戶的實際時間是不一樣是很正常的唁毒,所以Expires
在實際使用中會帶來一些麻煩蒜茴。 -
Cache-Control
這個字段是http 1.1
的規(guī)范,一般常用該字段的max-age
值來進(jìn)行判斷浆西,它是一個相對時間粉私,比如 .Cache-Control:max-age=3600
代表資源的有效期是 3600 秒。并且返回頭中的Date
表示消息發(fā)送的時間室谚,表示當(dāng)前資源在Date ~ Date +3600s
這段時間里都是有效的毡鉴。不過我在實際使用中常常遇到設(shè)置了max-age
之后,在max-age
時間內(nèi)重新訪問資源卻會返回304 not modified
秒赤,這是由于服務(wù)器的時間與本地的時間不同造成的。當(dāng)然Cache-Control
還有其他幾個值可以設(shè)置憎瘸, 不過相對來說都很少用了:-
no-cache
不使用本地緩存入篮。需要使用協(xié)商緩存。 -
no-store
直接禁止瀏覽器緩存數(shù)據(jù)幌甘,每次請求資源都會向服務(wù)器要完整的資源潮售, 類似于network
中的disabled cache
。 -
public
可以被所有用戶緩存锅风,包括終端用戶和 cdn 等中間件代理服務(wù)器酥诽。 -
private
只能被終端用戶的瀏覽器緩存。
-
如果 Cache-Control
與 Expires
同時存在的話皱埠, Cache-Control
的優(yōu)先級高于 Expires
肮帐。
協(xié)商緩存
協(xié)商緩存是由服務(wù)器來確定緩存資源是否可用。 主要涉及到兩對屬性字段边器,都是成對出現(xiàn)的训枢,即第一次請求的響應(yīng)頭帶上某個字, Last-Modified
或者 Etag
,則后續(xù)請求則會帶上對應(yīng)的請求字段 If-Modified-Since
或者 If-None-Match
忘巧,若響應(yīng)頭沒有 Last-Modified
或者 Etag
字段恒界,則請求頭也不會有對應(yīng)的字段。
Last-Modified/If-Modified-Since
二者的值都是 GMT 格式的時間字符串砚嘴,Last-Modified
標(biāo)記最后文件修改時間十酣, 下一次請求時,請求頭中會帶上If-Modified-Since
值就是Last-Modified
告訴服務(wù)器我本地緩存的文件最后修改的時間际长,在服務(wù)器上根據(jù)文件的最后修改時間判斷資源是否有變化耸采, 如果文件沒有變更則返回304 Not Modified
,請求不會返回資源內(nèi)容也颤,瀏覽器直接使用本地緩存洋幻。當(dāng)服務(wù)器返回304 Not Modified
的響應(yīng)時,response header
中不會再添加的Last-Modified
去試圖更新本地緩存的Last-Modified
翅娶, 因為既然資源沒有變化文留,那么Last-Modified
也就不會改變好唯;如果資源有變化,就正常返回返回資源內(nèi)容燥翅,新的Last-Modified
會在response header
返回骑篙,并在下次請求之前更新本地緩存的Last-Modified
,下次請求時森书,If-Modified-Since
會啟用更新后的Last-Modified
靶端。Etag/If-None-Match
, 值都是由服務(wù)器為每一個資源生成的唯一標(biāo)識串凛膏,只要資源有變化就這個值就會改變杨名。服務(wù)器根據(jù)文件本身算出一個哈希值并通過ETag
字段返回給瀏覽器,接收到If-None-Match
字段以后猖毫,服務(wù)器通過比較兩者是否一致來判定文件內(nèi)容是否被改變台谍。與Last-Modified
不一樣的是,當(dāng)服務(wù)器返回304 Not Modified
的響應(yīng)時吁断,由于在服務(wù)器上ETag
重新計算過趁蕊,response header
中還會把這個ETag
返回,即使這個ETag
跟之前的沒有變化仔役。
HTTP 中并沒有指定如何生成 ETag掷伙,可以由開發(fā)者自行生成,哈希是比較理想的選擇又兵。
為什么要有 Etag
HTTP1.1
中 Etag
的出現(xiàn)主要是為了解決幾個 Last-Modified
比較難解決的問題:
- 一些文件也許會周期性的更改任柜,但是內(nèi)容并不改變(僅僅改變的修改時間),這個時候我們并不希望客戶端認(rèn)為這個文件被修改了寒波,而重新 GET乘盼;
- 某些文件修改非常頻繁,比如在秒以下的時間內(nèi)進(jìn)行修改俄烁,(比方說 1s 內(nèi)修改了 N 次)绸栅,
If-Modified-Since
能檢查到的粒度是秒級的,使用Etag
就能夠保證這種需求下客戶端在 1 秒內(nèi)能刷新 N 次 cache页屠。 - 某些服務(wù)器不能精確的得到文件的最后修改時間粹胯。
優(yōu)先級
Cache-Control > expires > Etag > Last-Modified
用戶行為對緩存的影響
簡單說就是 F5 刷新的時候,會暫時禁用強緩存
經(jīng)過對 qq辰企、fire fox 风纠、safari 、chrome 這幾個瀏覽器的訪問同一個頁面測試我發(fā)現(xiàn)牢贸,不同的瀏覽器在 F5 刷新的時候 竹观,同一個文件 qq 、fire fox 瀏覽器會返回 304 Not Nodified
,在請求頭中不攜帶 Expires/Cache-Control
臭增; 而 chrome 和 safari 刷新的時候懂酱,會返回 200 from cache
, 沒有真正發(fā)起請求誊抛,走強緩存列牺。可見不同的瀏覽器反饋是不一致的拗窃,所以下面表格中"F5 刷新"時 Expires/Cache-Control
會無效我認(rèn)為是存在一定爭議的瞎领。
而 Ctrl + F5 強制刷新的時候,會暫時禁用強緩存和協(xié)商緩存随夸。
在寫這篇博客時九默,對于我僅僅測試了一個瀏覽器之后便寫了無效(因為網(wǎng)上大多數(shù)帖子寫了無效,我也以為我驗證通過了)逃魄,對指出這個問題的群友荤西,表示感謝,希望其他人不會被我誤導(dǎo)伍俘。
用戶操作 | Expires/Cache-Control | Last-Modied/Etag |
---|---|---|
地址欄回車 | 有效 | 有效 |
頁面鏈接跳轉(zhuǎn) | 有效 | 有效 |
新開窗口 | 有效 | 有效 |
前進(jìn)回退 | 有效 | 有效 |
F5 刷新 | 無效(有爭議,不同瀏覽器反饋不一致) | 有效 |
Ctrl+F5 強制刷新 | 無效 | 無效 |
如何設(shè)置強緩存和協(xié)商緩存
-
后端服務(wù)器勉躺,寫入代碼邏輯中:
res.setHeader('max-age': '3600 public') res.setHeader(etag: '5c20abbd-e2e8') res.setHeader('last-modified': Mon, 24 Dec 2018 09:49:49 GMT)
-
Nginx
配置add_header Cache-Control "max-age=3600"
一般來說癌瘾,通過 nginx 靜態(tài)資源服務(wù)器,會默認(rèn)給資源帶上強緩存饵溅、協(xié)商緩存的 header 字段妨退。
[圖片上傳失敗...(image-beeb56-1580376444420)]
兩個示例
-
如果在
cache-control
定義的max-age
時間之內(nèi),js
,css
文件會走強緩存蜕企,http
狀態(tài)碼是 200咬荷, 跟服務(wù)器也并不會有交互。但是第一個文件index.html
文件, 每次回車或者刷新都是狀態(tài)碼都是 304 轻掩,因為它的請求頭中默認(rèn)每次都攜帶了Cache-Control: max-age=0
幸乒。[圖片上傳失敗...(image-acbcab-1580376444420)]
[圖片上傳失敗...(image-6f9b34-1580376444420)]
-
js
css
文件cache-control
超時之后,重新按回車會走協(xié)商緩存唇牧,請求服務(wù)器發(fā)現(xiàn)資源沒有改變罕扎,于是返回 304 ,瀏覽器從緩存中獲取內(nèi)容丐重,從size
中也可以看出端倪腔召, 幾百 B 的包不是靜態(tài)資源的體積。[圖片上傳失敗...(image-882bd2-1580376444420)]
三級緩存原理(大白話)
最后總結(jié)一下瀏覽器的三級緩存原理:
先去內(nèi)存看扮惦,如果有臀蛛,直接加載
如果內(nèi)存沒有,擇取硬盤獲取,如果有直接加載
如果硬盤也沒有浊仆,那么就進(jìn)行網(wǎng)絡(luò)請求
加載到的資源緩存到硬盤和內(nèi)存