關於 http 緩存,這些知識點你可能都不懂

  • 在地址欄輸入 url 回車,強緩存何時失效,何時有效,你知道嗎?
  • disk cache 和 memory cache 是什麼?在哪裏出現?
  • 火狐、IE 的緩存處理策略和 Chrome 同樣嗎?
  • cache-controlpublicprivate 你知道何時使用嗎?
  • 你知道 ETag 可能也有不適用的時候嗎?
  • 你知道 http 頭中 Vary 和緩存有怎樣的關係嗎?

若是上面的問題你都懂了,那關掉這個頁面吧;若是你喜歡本文,點一個贊吧~css

先小小的回顧下html

http header 描述 強緩存 協商緩存
Pragma 老版本的本地緩存機制,http 1.0 及如下
Expires 在此時候以後,響應過時,時間是絕對時間,受本地時間影響 *
Cache-Control 強緩存策略 Cache-Control: public, max-age=31536000, must-revalidate max-age是相對時間 *
Last-ModifiedIf-Modified-Since 資源最後被更改的時間,精確到秒 *
ETagIf-None-Match 資源的標識值,用來惟一的標識一個資源 *

處理優先級git

在本地 Cache-Control > ExpiresPragma 在不支持 Cache-Control 時生效。github

若是本地緩存過時,則要依靠協商緩存算法

ETag > Last-Modified瀏覽器

強緩存的 http 狀態碼是 200 OK緩存

協商緩存的 http 狀態碼是 304 Not Modifiedbash

Cache-Control

  • public 代表響應能夠被任何對象(包括:發送請求的客戶端,代理服務器,等等)緩存。
  • private 代表響應只能被單個用戶緩存,不能做爲共享緩存(即代理服務器不能緩存它)。私有緩存能夠緩存響應內容。
  • no-cache 即便有緩存也會向服務器發請求。
  • no-store 讓客戶端不要把資源存在緩存。
  • max-age=<seconds> 設置緩存存儲的最大週期,超過這個時間緩存被認爲過時(單位秒)。與 Expires 相反,時間是相對於請求的時間。
  • s-maxage=<seconds> 覆蓋 max-age 或者 Expires 頭,可是僅適用於共享緩存(好比各個代理),私有緩存會忽略

問題 關於 publicprivate 的區別 服務器

image

翻譯成中文:private 不容許代理緩存。舉個例子,ISP 服務商能夠在你的客戶端和互聯網之間加上不可見的代理,這個代理會緩存網頁來下降帶寬,客戶端設置 cache-control: private 以後,能夠指定 ISP 代理不容許緩存網頁,可是容許最後的接受者緩存。而使用 cache-control: public 的意思是說,誰均可以緩存哈,因此中間代理會緩存一份以減小帶寬下降費用。網絡

若是是誰均可以訪問的內容,好比網站 logo,那就用 public 好了,反正也不會有關鍵數據泄露風險,儘可能中間代理都給緩存上,減小帶寬。而若是是一個含有用戶信息的頁面,好比頁面含有個人用戶名,那這個頁面固然不是對誰都有用,由於不一樣的人要返回不一樣的用戶名嘛,這個時候 private 會適合一些,若是代理緩存了個人這個頁面,別的用戶訪問又會緩存別人的,這顯然不合理,並且你的我的私密數據也儘可能不要被保存在不受信任的地方。

固然,全部不被表示爲 public 的數據都應該被標識爲 private,要否則數據會存儲在中間服務器上,別人就有可能會訪問到這個數據。

禁止緩存

Cache-Control: no-cache, no-store, must-revalidate

緩存靜態資源

Cache-Control:public, max-age=86400

ETag、If-Match

ETagIf-None-Match 常被用來處理協商緩存。而 ETagIf-Match 能夠 避免「空中碰撞」

ETag HTTP響應頭是資源的特定版本的標識符。這可讓緩存更高效,並節省帶寬,由於若是內容沒有改變,Web服務器不須要發送完整的響應。而若是內容發生了變化,使用 ETag 有助於防止資源的同時更新相互覆蓋(「空中碰撞」)。

當編輯 MDN 時,當前的 Wiki 內容被散列,並在響應中放入Etag

ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4 複製代碼

將更改保存到 Wiki 頁面(發佈數據)時,POST 請求將包含有 ETag 值的 If-Match 頭來檢查是否爲最新版本。

If-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"
複製代碼

若是哈希值不匹配,則意味着文檔已經被編輯,拋出 412 ( Precondition Failed) 前提條件失敗錯誤。

If-None-Match 是客戶端發送給服務器時的請求頭,其值是服務器返回給客戶端的 ETag,當 If-None-Match 和服務器資源最新的 Etag 不一樣時,返回最新的資源及其 Etag

Last-Modified、If-Modified-Since

Last-ModifiedIf-Modified-Since 是資源最後更改的時間。

Last-Modified: <day-name>, <day> <month> <year> <hour>:<minute>:<second> GMT

Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT 
複製代碼

這兩個的區別是: Last-Modified 是服務器發送給客戶端的,If-Modified-Since 是客戶端發送給服務器的。

問題 Last-Modified 機制和 ETag 機制的區別和優先級是怎樣的?

Last-Modified 只能精確到秒,因此在秒級如下的更改沒法檢測到。而 ETag 能夠表徵文件的任何更改,只要文件變化 ETag 就會變化。因此 Last-Modified 是一個備用機制,優先級不如 Etag

客戶端請求帶有 If-None-Match 在服務端校驗匹配則返回 304,校驗不匹配則返回 200,同時返回最新的資源和 Etag

Age

Age 消息頭裏包含消息對象在緩存代理中存貯的時長,以秒爲單位。

Age 消息頭的值一般接近於0。表示此消息對象剛剛從原始服務器獲取不久;其餘的值則是表示代理服務器當前的系統時間與此應答消息中的通用消息頭 Date 的值之差。

Age: <delta-seconds>
複製代碼

age: 1135860
複製代碼

Date

Date 是一個通用首部,其中包含了報文建立的日期和時間。

指的是響應生成的時間. 請求通過代理服務器時, 返回的 Date 未必是最新的, 一般這個時候, 代理服務器將增長一個 Age 字段告知該資源已緩存了多久.

Date: Wed, 21 Oct 2015 07:28:00 GMT 
複製代碼

Vary

Vary 是一個HTTP響應頭部信息,它決定了對於將來的一個請求頭,應該用一個緩存的回覆(response)仍是向源服務器請求一個新的回覆。它被服務器用來代表在 content negotiation algorithm(內容協商算法)中選擇一個資源表明的時候應該使用哪些頭部信息(headers)。

對於服務器而言, 資源文件可能不止一個版本, 好比說壓縮和未壓縮, 針對不一樣的客戶端, 一般須要返回不一樣的資源版本。 好比說老式的瀏覽器可能不支持解壓縮, 這個時候, 就須要返回一個未壓縮的版本; 對於新的瀏覽器, 支持壓縮, 返回一個壓縮的版本, 有利於節省帶寬, 提高體驗. 那麼怎麼區分這個版本呢, 這個時候就須要 Vary 了。

服務器經過指定 Vary: Accept-Encoding, 告知代理服務器, 對於這個資源, 須要緩存兩個版本: 壓縮和未壓縮. 這樣老式瀏覽器和新的瀏覽器, 經過代理, 就分別拿到了未壓縮和壓縮版本的資源, 避免了都拿同一個資源的尷尬。

Vary: Accept-Encoding,User-Agent
複製代碼

如上設置, 代理服務器將針對是否壓縮和瀏覽器類型兩個維度去緩存資源. 如此一來, 同一個url, 就能針對 PC 和 Mobile 返回不一樣的緩存內容。

怎麼讓瀏覽器不緩存靜態資源

能夠設置 Cache-Control

Cache-Control: no-cache, no-store, must-revalidate
複製代碼

也能夠給資源增長版本號,這樣能夠很方便地控制何時加載最新資源,這也是目前作版本更新比較經常使用的手段,即便老資源還在有效期內,加上了 query、hash。

<link rel="stylesheet" type="text/css" href="../css/style.css?version=1.8.9"/>
複製代碼

用戶行爲與緩存

用戶按 f5(ctrl+r)、ctrl+f五、點擊前進後退 都會觸發緩存機制

通過本地測試發現和網上傳的有些出入,記錄以下(強緩存有效表明直接使用本地的緩存文件,Chrome 狀態碼爲 200,協商緩存有效表明向服務器發起請求,服務器可能返回 304 無內容或者 200 有內容)。

操做 強緩存 協商緩存
頁面連接跳轉 有效 有效
新開窗口 有效 有效
前進後退 有效 有效
地址欄回車 失效 或者 有效 有效
ctrl+r或者 f5 失效 有效
ctrl+f5 強制刷新 失效 失效

image

地址欄回車和網絡上不同,打個比方,若是當前已經在 http://localhost:3333/,而後在地址欄選中後回車,你會發現沒有緩存。

image

可是若是當前不在 http://localhost:3333/ ,好比 http://localhost:3333/index.css 或者空白頁,而後輸入 http://localhost:3333/ 回車,這時候就會直接從本地緩存中讀取。

image

驚喜不,意外不

image

關於 memory cache 和 disk cache

這兩種緩存類型存在於 Chrome 中。

上個小標題 用戶行爲與緩存,咱們看到瀏覽器從本地讀緩存的時候有一個 disk cache,與之對應的還有一個 memory cache。看名字也能大概纔出來,disk cache 是從硬盤中讀取的文件緩存,memory cache 是從內存中直接讀取的內容,速度上固然也是後者更快。

那爲何要有這兩種緩存的形式呢?

disk cache 存在硬盤,能夠存不少,容量上限比內容緩存高不少,而 memory cache 從內存直接讀取,速度上佔優點,這兩個各有各的好處!

問題 瀏覽器如何決策使用哪一種緩存呢?

image

來自知乎 瀏覽器是根據什麼決定「from disk cache」與「from memory cache」?

劃重點!!關於 Chrome、FF、IE 的緩存區別

這個內容網絡上不多有文章介紹,通過我測試以後發現區別挺大的。Chome 的緩存處理和另外兩個存在明顯的不一樣!

上面講的強緩存、memory cache、disk cache 在 FF、IE 中都沒有明顯的區分,或許這就是 Chrome 速度快的緣由?

咱們舉個例子,屢次重複請求 https://www.baidu.com/,查看三個瀏覽器的區別。

Chrome

image

FF

image

IE

image

Chrome 中有強緩存、協商緩存,強緩存分爲 memory cache、disk cache,這種處理機制能夠最大化的利用緩存,減小發起的請求,從而優化頁面加載速度!!Chrome 資源狀態碼都是 200,沒有 304,另外兩家都存在大量304,在 FF、IE 中,即便服務器明確表示資源須要進行強緩存,好比 Cache-Control: max-age=86400,他們仍然不會應用所謂的強緩存策略,仍然會向服務器發送請求,服務器返回 304 ,告訴瀏覽器用你本地的資源就行了!!!

怎麼知道這個請求確實是發送到了代理或者真實服務器呢?咱們上面有說 AgeDate,Age 表示當前請求在返回時,在代理那裏的時間減去 Date 的時間,因此每次只要請求發出去了,Age 都會相比上一次增長!!!

咱們以掘金的 https://b-gold-cdn.xitu.io/v3/static/js/0.81b469df7a0dd87832a4.js 文件爲例。在 FF 上,前一次的結果是

image

刷一下

image

Date 沒有變,表示都是使用的同一個真實服務器的響應資源,Age 後一次比前一次變大了,可是狀態碼都是 304,而緩存規則是 cache-control: s-maxage=2592199, max-age=2592199!!!

因此想要作好緩存,須要考慮瀏覽器兼容的問題,綜合使用 http headers。

既然 Etag 能夠校驗資源是否更改,那爲何還要 Last-Modified 做爲備用策略

這個問題大多數講緩存的文章也沒有說起。

Etag 是經過資源內容生成的,因此會有一個計算成本存在,本如大圖片的更改,它的最後更改時間能夠很容易得到,可是計算 Etag 成本就會高不少了。


參考


喜歡的話,給個點贊吧~

歡迎你們關注個人掘金和公衆號,算法、TypeScript、React 及其生態源碼按期講解。

相關文章
相關標籤/搜索