原文地址:關於瀏覽器快取我知道多少
歡迎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
中的資訊判斷是否命中強快取,如果命中則直接使用快取中的資源不會再向伺服器傳送請求。
從圖中可以看出,強快取一般是這樣一個流程:
- 檢視
header
頭中的Expire
和Cache-control
來判斷是否滿足規則; - 如果滿足規則,就返回快取的資料;
- 如果不滿足規則,就向伺服器傳送請求;
- 伺服器返回資料;
- 將新資料存入快取。
所以我們主要就是關注 Expire
和 Cache-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:不能被多使用者共享。
現在基本上都會同時設定 Expire
和 Cache-Control
,Cache-Control
的優先順序別更高。
協商快取
當強快取沒有命中的時候,瀏覽器會傳送一個請求到伺服器,伺服器根據請求頭中的部分資訊來判斷是否命中快取。如果命中,則返回 304
,告訴瀏覽器資源未更新,可使用本地的快取。
從圖中可以看出,協商快取一般是這樣一個流程:
- 把資源標識,比如
If-Modify-Since
或Etag
傳送到伺服器,確認資源是否更新; - 如果資源未更新,請求響應返回的http狀態為
304
並且會顯示一個Not Modified
的字串,告訴瀏覽器使用本地快取; - 如果資源已經更新,返回新的資料;
- 將新資料存入快取。
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-Modified
和 Etag
是可以同時設定的,伺服器會優先校驗 Etag
,如果 Etag
相等就會繼續比對 Last-Modified
,最後才會決定是否返回 304
。
總結
當瀏覽器再次訪問一個已經訪問過的資源時,它會這樣做:
- 看看是否命中強快取,如果命中,就直接使用快取了;
- 如果沒有命中強快取,就發請求到伺服器檢查是否命中協商快取;
- 如果命中協商快取,伺服器會返回
304
告訴瀏覽器使用本地快取; - 否則,返回最新的資源。