關於瀏覽器快取我知道多少

axuebin發表於2017-11-01

原文地址:關於瀏覽器快取我知道多少

歡迎star。

如果有錯誤的地方歡迎指正。


在前端開發中,我們在提到效能優化的時候總會提到一點:合理設定快取。我們該如何從這方面入手來考慮提高網站效能呢?

前言

我們都知道 HTML5 引入了應用程式快取,可以在沒有網路的情況下進行訪問,同時,HTML5 還引入了 storage 本地儲存。這些都屬於應用快取。

本篇文章主要內容是和瀏覽器快取相關的,也可以說是 HTTP 快取。

什麼是瀏覽器快取

MDN 上是這樣解釋瀏覽器快取的:

A browser cache holds all documents downloaded via HTTP by the user ... without requiring an additional trip to the server.

意思就是,瀏覽器快取儲存著使用者通過 HTTP 獲取的所有資源,再下一次請求時可以避免重複向伺服器發出多餘的請求。

通俗的說,就是在你訪問過一次某個網站之後,這個站點的文字、圖片等所有資源都被下載到本地了,下次再訪問該網站時判斷是否滿足快取條件,如果滿足就不用再花費時間去等待資源的獲取了。

瀏覽器快取的分類

一般來說瀏覽器快取可以分為兩類:

  • 強快取
  • 協商快取(對比快取)

我們需要知道的是,瀏覽器在載入資源時,會先判斷是否命中強快取再驗證是命中協商快取

其它的的具體細節,稍後會展開來說。

強快取

瀏覽器在載入資源時,會先根據本地快取資源的 header 中的資訊判斷是否命中強快取,如果命中則直接使用快取中的資源不會再向伺服器傳送請求。

從圖中可以看出,強快取一般是這樣一個流程:

  1. 檢視 header 頭中的 ExpireCache-control 來判斷是否滿足規則;
  2. 如果滿足規則,就返回快取的資料;
  3. 如果不滿足規則,就向伺服器傳送請求;
  4. 伺服器返回資料;
  5. 將新資料存入快取。

所以我們主要就是關注 ExpireCache-control 這兩個欄位。

Expire

同樣地,我們看看MDN中如何解釋這個欄位:

The Expires header contains the date/time after which the response is considered stale.

這個欄位包含了一個時間,過了這個時間,響應將會失效。

也就是說,Expire 這個欄位表示快取到期時間,我們來開啟一個網站並檢視 Response Header 看看這個欄位:

Expires:Fri, 27 Oct 2017 07:55:30 GMT複製程式碼

可能在你檢視這的時候發現時間不對啊,怎麼都已經是過去了 ~

GMT 表示的是格林威治時間,和北京時間相差8小時。

上面的這個時間表示的是 2017年10月27日15:55:30

通過設定 Expire 來設定快取有一個致命缺點:

可以看出,這個是個絕對時間,也就是說,如果我修改了客戶端的本地時間,是不是就會導致判斷快取失效了呢。

Cache-Control

既然不能設定絕對時間,那我就設定個相對時間唄。

HTTP/1.1 中,增加了一個欄位 Cache-Control ,它包含一個 max-age 屬性,該欄位表示資源快取的最大有效時間,這就是一個相對時間。

Cache-Control:max-age=600複製程式碼

這個表示的就是最大有效時間是 600s ,對的,它的單位是秒。

Cache-Control 除了 max-age 屬性之外還有一些屬性:

  • no-cache:需要進行協商快取,傳送請求到伺服器確認是否使用快取。
  • no-store:禁止使用快取,每一次都要重新請求資料。
  • public:預設設定。
  • private:不能被多使用者共享。

現在基本上都會同時設定 ExpireCache-ControlCache-Control 的優先順序別更高。

協商快取

當強快取沒有命中的時候,瀏覽器會傳送一個請求到伺服器,伺服器根據請求頭中的部分資訊來判斷是否命中快取。如果命中,則返回 304 ,告訴瀏覽器資源未更新,可使用本地的快取。

從圖中可以看出,協商快取一般是這樣一個流程:

  1. 把資源標識,比如 If-Modify-SinceEtag 傳送到伺服器,確認資源是否更新;
  2. 如果資源未更新,請求響應返回的http狀態為 304 並且會顯示一個 Not Modified 的字串,告訴瀏覽器使用本地快取;
  3. 如果資源已經更新,返回新的資料;
  4. 將新資料存入快取。

Last-Modified,If-Modified-Since

瀏覽器第一次請求資源的時候,伺服器返回的 header 上會帶有一個 Last-Modified 欄位,表示資源最後修改的時間。

Last-Modified: Fri, 27 Oct 2017 07:55:30 GMT複製程式碼

同樣的,這是一個 GMT 的絕對時間。

當瀏覽器再次請求該資源時,請求頭中會帶有一個 If-Modified-Since 欄位,這個值是第一次請求返回的 Last-Modified 的值。伺服器收到這個請求後,將 If-Modified-Since 和當前的 Last-Modified 進行對比。如果相等,則說明資源未修改,返回 304,瀏覽器使用本地快取。

well,這個方法也是有缺點的:

  • 最小單位是秒。也就是說如果我短時間內資源發生了改變,Last-Modified 並不會發生變化;
  • 週期性變化。如果這個資源在一個週期內修改回原來的樣子了,我們認為是可以使用快取的,但是 Last-Modified 可不這樣認為。

所以,後來又引入一個 Etag

Etag

Etag 一般是由檔案內容 hash 生成的,也就是說它可以保證資源的唯一性,資源發生改變就會導致 Etag 發生改變。

同樣地,在瀏覽器第一次請求資源時,伺服器會返回一個 Etag 標識。當再次請求該資源時, 會通過 If-no-match 欄位將 Etag 傳送回伺服器,然後伺服器進行比較,如果相等,則返回 304 表示未修改。

Last-ModifiedEtag 是可以同時設定的,伺服器會優先校驗 Etag,如果 Etag 相等就會繼續比對 Last-Modified,最後才會決定是否返回 304

總結

當瀏覽器再次訪問一個已經訪問過的資源時,它會這樣做:

  1. 看看是否命中強快取,如果命中,就直接使用快取了;
  2. 如果沒有命中強快取,就發請求到伺服器檢查是否命中協商快取;
  3. 如果命中協商快取,伺服器會返回 304 告訴瀏覽器使用本地快取;
  4. 否則,返回最新的資源。

相關文章