了解 HTTP 緩存嗎?有關(guān) HTTP 緩存的首部字段說一下 ?
本文轉(zhuǎn)載自微信公眾號(hào)「三分鐘學(xué)前端」,作者sisterAn。轉(zhuǎn)載本文請聯(lián)系三分鐘學(xué)前端公眾號(hào)。
常見的 HTTP 緩存首部字段有:
- Expires:響應(yīng)頭,代表該資源的過期時(shí)間
- Cache-Control:請求/響應(yīng)頭,緩存控制字段,精確控制緩存策略
- If-Modified-Since:請求頭,資源最近修改時(shí)間,由瀏覽器告訴服務(wù)器
- Last-Modified:響應(yīng)頭,資源最近修改時(shí)間,由服務(wù)器告訴瀏覽器
- Etag:響應(yīng)頭,資源標(biāo)識(shí),由服務(wù)器告訴瀏覽器
- If-None-Match:請求頭,緩存資源標(biāo)識(shí),由瀏覽器告訴服務(wù)器
其中, 強(qiáng)緩存 :
- Expires(HTTP/1.0)
- Cache-Control(HTTP/1.1)
協(xié)商緩存:
- Last-Modified 和 If-Modified-Since(HTTP/1.0)
- ETag 和 If-None-Match(HTTP/1.1)
緩存過程分析
瀏覽器與服務(wù)器通信的方式為應(yīng)答模式,即瀏覽器發(fā)起 HTTP 請求,服務(wù)器響應(yīng)請求。在瀏覽器第一次發(fā)起請求時(shí),會(huì)根據(jù)響應(yīng)報(bào)文中 HTTP 頭的緩存標(biāo)識(shí),決定是否緩存結(jié)果,如果是則將請求結(jié)果和緩存標(biāo)識(shí)存入瀏覽器緩存中,簡單的過程如下圖:
由上圖我們可以知道:
- 瀏覽器每次發(fā)起請求,都會(huì)先在瀏覽器緩存中查找該請求的結(jié)果以及緩存標(biāo)識(shí)
- 瀏覽器每次從服務(wù)器端拿到返回的請求結(jié)果,都會(huì)將該結(jié)果和緩存標(biāo)識(shí)存入瀏覽器緩存中
以上兩點(diǎn)結(jié)論就是瀏覽器緩存機(jī)制的關(guān)鍵,它確保了每個(gè)請求的緩存存入與讀取,只要我們再理解瀏覽器緩存的使用規(guī)則,那么所有的問題就迎刃而解了
本文也將圍繞著這點(diǎn)進(jìn)行詳細(xì)分析。為了方便大家理解,這里我們根據(jù)是否需要向服務(wù)器重新發(fā)起HTTP請求將緩存過程分為兩個(gè)部分,分別是:
- 強(qiáng)緩存: 向?yàn)g覽器緩存查找該請求結(jié)果,并根據(jù)該結(jié)果的緩存規(guī)則來決定是否使用該緩存結(jié)果的過程
- 協(xié)商緩存: 強(qiáng)緩存失效后,瀏覽器攜帶緩存標(biāo)識(shí)向服務(wù)器發(fā)起請求,由服務(wù)器根據(jù)緩存標(biāo)識(shí)決定是否使用緩存的過程
強(qiáng)緩存(緩存控制)
強(qiáng)緩存表示在緩存期間是否使用緩存(緩存是否有效),需不需要重新發(fā)送HTTP請求
控制強(qiáng)緩存的字段分別是 Expires 和 Cache-Control ,其中 Cache-Control 優(yōu)先級比 Expires 高
Expires(HTTP/1.0)
值為服務(wù)器返回該請求結(jié)果緩存的到期時(shí)間:
- Expires: Wed, 22 Oct 2018 08:41:00 GMT
表示資源會(huì)在 Wed, 22 Oct 2018 08:41:00 GMT 后過期,需要再次請求。
并且 Expires 受限于客戶端時(shí)間,如果修改了客戶端時(shí)間,可能會(huì)造成緩存失效。
所以現(xiàn)在 HTTP/1.1中新增了 Cache-Control
Cache-Control(HTTP/1.1)
- Cache-control: max-age=30
該屬性值表示資源會(huì)在 30 秒后過期,需要再次請求。也就是說在 30 秒內(nèi)如果再次發(fā)起該請求,則會(huì)直接使用緩存,強(qiáng)緩存生效。
它與 Expires 相比:
- HTTP響應(yīng)報(bào)文中 Expires 的時(shí)間值,是一個(gè)絕對值
- HTTP響應(yīng)報(bào)文中 Cache-Control為max-age=600 ,是相對值(解決 Expires 受限于客戶端時(shí)間)
除了 max-age ,它還有以下取值:
注意下面的 no-cache ,資源依然會(huì)被緩存,并且這個(gè)緩存要服務(wù)器驗(yàn)證后才可以使用
max-age=0 和 no-cache 等價(jià)嗎?
從規(guī)范的字面意思來說,max-age 到期是 應(yīng)該(SHOULD) 重新驗(yàn)證,而 no-cache 是 必須(MUST) 重新驗(yàn)證。但實(shí)際情況以瀏覽器實(shí)現(xiàn)為準(zhǔn),大部分情況他們倆的行為還是一致的。(如果是 max-age=0, must-revalidate 就和 no-cache 等價(jià)了)
總結(jié)
自從 HTTP/1.1 開始,Expires 逐漸被 Cache-Control 取代。Cache-Control 是一個(gè)相對時(shí)間,即使客戶端時(shí)間發(fā)生改變,相對時(shí)間也不會(huì)隨之改變,這樣可以保持服務(wù)器和客戶端的時(shí)間一致性。而且 Cache-Control 的可配置性比較強(qiáng)大。
Cache-Control 的優(yōu)先級高于 Expires,為了兼容 HTTP/1.0 和 HTTP/1.1,實(shí)際項(xiàng)目中兩個(gè)字段我們都會(huì)設(shè)置。
協(xié)商緩存(緩存校驗(yàn))
如果緩存過期了:
- 沒有 Cache-Control 和 Expires
- Cache-Control 和 Expires 過期
- 設(shè)置了 no-cache
需要發(fā)起請求驗(yàn)證服務(wù)器資源是否有更新:
- 有更新,返回200,更新緩存
- 無更新,返回304,更新瀏覽器緩存有效期
Last-Modified 和 If-Modified-Since(HTTP/1.0)
- Last-Modified(響應(yīng)頭)
- If-Modified-Since(請求頭)
Last-Modified 表示本地文件最后修改日期,If-Modified-Since 會(huì)將 Last-Modified 的值發(fā)送給服務(wù)器,詢問服務(wù)器在該日期后資源是否有更新,有更新的話就會(huì)將新的資源發(fā)送回來,否則返回 304 狀態(tài)碼。
但是這種方式存在著一些缺點(diǎn),例如:
- 負(fù)載均衡的服務(wù)器,各個(gè)服務(wù)器生成的 Last-Modified 可能有所不同
- GMT 格式有最小單位,例如,如果在一秒內(nèi)有更改將不能被識(shí)別
ETag 和 If-None-Match(HTTP/1.1)
為了解決上面的那個(gè)問題, HTTP/1.1 加了這組標(biāo)記
- ETag(響應(yīng)頭)
- If-None-Match(請求頭)
ETag 類似于文件指紋,是文件的一個(gè)唯一標(biāo)識(shí)序列,當(dāng)資源有變化時(shí),Etag就會(huì)重新生成,If-None-Match 會(huì)將當(dāng)前 ETag 發(fā)送給服務(wù)器,詢問該資源 ETag 是否變動(dòng),有變動(dòng)的話就將新的資源發(fā)送回來。并且 ETag 優(yōu)先級比 Last-Modified 高
使用 ETag 就可以精確地識(shí)別資源的變動(dòng)情況,就算是秒內(nèi)的更新,也會(huì)讓瀏覽器感知,能夠更有效地利用緩存
ETag 強(qiáng)弱之分
ETag 機(jī)制同時(shí)支持強(qiáng)校驗(yàn)和弱校驗(yàn)。它們通過ETag標(biāo)識(shí)符的開頭是否存在“W/”來區(qū)分,如:
- "123456789" -- 一個(gè)強(qiáng)ETag驗(yàn)證符
- W/"123456789" -- 一個(gè)弱ETag驗(yàn)證符
強(qiáng) ETag 要求資源在字節(jié)級別必須完全相符,弱 ETag 在值前有個(gè)“W/”標(biāo)記,只要求資源在語義上沒有變化,但內(nèi)部可能會(huì)有部分發(fā)生了改變(例如 HTML 里的標(biāo)簽順序調(diào)整,或者多了幾個(gè)空格)
Vary 響應(yīng)
服務(wù)器通過指定 Vary: Accept-Encoding ,告知代理服務(wù)器,對于這個(gè)資源,需要緩存兩個(gè)版本:
- 壓縮
- 未壓縮
這樣老式瀏覽器和新的瀏覽器, 通過代理, 就分別拿到了未壓縮和壓縮版本的資源,避免了都拿同一個(gè)資源的尷尬。
- Vary: Accept-Encoding, User-Agent
如上設(shè)置,代理服務(wù)器將針對是否壓縮和瀏覽器類型兩個(gè)維度去緩存資源。如此一來,同一個(gè)url,就能針對 PC 和 Mobile 返回不同的緩存內(nèi)容。
來自:https://github.com/Advanced-Frontend/Daily-Interview-Question