瀏覽器快取作為效能最佳化的重要一環,對於前端而言,重要性不言而喻。以前總是一知半解的,所以這次好好整理總結了一下。
1、快取機制
首先我們來總體感知一下它的匹配流程,如下:
- 瀏覽器傳送請求前,根據請求頭的expires和cache-control判斷是否命中(包括是否過期)強快取策略,如果命中,直接從快取獲取資源,並不會傳送請求。如果沒有命中,則進入下一步。
- 沒有命中強快取規則,瀏覽器會傳送請求,根據請求頭的last-modified和etag判斷是否命中協商快取,如果命中,直接從快取獲取資源。如果沒有命中,則進入下一步。
- 如果前兩步都沒有命中,則直接從服務端獲取資源。
2、強快取
強快取:不會向伺服器傳送請求,直接從快取中讀取資源。
2.1 強快取原理
強制快取就是向瀏覽器快取查詢該請求結果,並根據該結果的快取規則來決定是否使用該快取結果的過程,強制快取的情況主要有三種(暫不分析協商快取過程),如下:
- 第一次請求,不存在快取結果和快取標識,直接向伺服器傳送請求
- 存在快取標識和快取結果,但是已經失效,強制快取是啊比,則使用協商快取(暫不分析)
- 存在該快取結果和快取標識,且該結果尚未失效,強制快取生效,直接傳回該結果
那麼強制快取的快取規則是什麼?
當瀏覽器向伺服器發起請求時,伺服器會將快取規則放入HTTP響應報文的HTTP頭中和請求結果一起傳回給瀏覽器,控制強制快取的欄位分別是Expires
和Cache-Control
,其中Cache-Control
優先順序比Expires
高。
2.1.1、 Expires
快取過期時間,用來指定資源到期的時間,是伺服器端的具體的時間點。也就是說,Expires=max-age + 請求時間
,需要和Last-modified
結合使用。Expires
是Web伺服器響應訊息頭欄位,在響應http請求時告訴瀏覽器在過期時間前瀏覽器可以直接從瀏覽器快取取資料,而無需再次請求。
Expires 是 HTTP/1 的產物,受限於本地時間,如果修改了本地時間,可能會造成快取失效。
2.1.2、 Cache-Control
在HTTP/1.1中,Cache-Control是最重要的規則,主要用於控制網頁快取,主要取值為:
- public:所有內容都將被快取(客戶端和代理伺服器都可快取)
- private:所有內容只有客戶端可以快取,
Cache-Control
的預設取值 - no-cache:客戶端快取內容,但是是否使用快取則需要經過協商快取來驗證決定
- no-store:所有內容都不會被快取,即不使用強制快取,也不使用協商快取
- max-age=xxx (xxx is numeric):快取內容將在xxx秒後失效
需要註意的是,
no-cache
這個名字有一點誤導。設定了no-cache
之後,並不是說瀏覽器就不再快取資料,只是瀏覽器在使用快取資料時,需要先確認一下資料是否還跟伺服器保持一致,也就是協商快取。而no-store
才表示不會被快取,即不使用強制快取,也不使用協商快取
2.1.3、設定
強快取需要服務端設定expires
和cache-control
。nginx
程式碼參考,設定了一年的快取時間:
location ~ .*\.(ico|svg|ttf|eot|woff)(.*) {
proxy_cache pnc;
proxy_cache_valid 200 304 1y;
proxy_cache_valid any 1m;
proxy_cache_lock on;
proxy_cache_lock_timeout 5s;
proxy_cache_use_stale updating error timeout invalid_essay-header http_500 http_502;
expires 1y;
}
瀏覽器的快取存放在哪裡,如何在瀏覽器中判斷強制快取是否生效?這就是下麵我們要講到的from disk cache
和from memory cache
。
2.2、from disk cache和from memory cache
細心地同學在開發的時候應該註意到了Chrome的網路請求的Size會出現三種情況from disk cache(磁碟快取)
、from memory cache(記憶體快取)
、以及資源大小數值。
狀態 | 型別 | 說明 |
---|---|---|
200 | form memory cache | 不請求網路資源,資源在記憶體當中,一般指令碼、字型、圖片會存在記憶體當中 |
200 | form disk ceche | 不請求網路資源,在磁碟當中,一般非指令碼會存在記憶體當中,如css等 |
200 | 資源大小數值 | 從伺服器下載最新資源 |
304 | 報文大小 | 請求服務端發現資源沒有更新,使用本地資源 |
瀏覽器讀取快取的順序為memory –> disk。
以訪問https://github.com/xiangxingchen/blog
為例
我們第一次訪問時
https://github.com/xiangxingchen/blog
關閉標簽頁,再此開啟
https://github.com/xiangxingchen/blog
時
F5掃清時
簡單的對比一下
3、協商快取
協商快取就是強制快取失效後,瀏覽器攜帶快取標識向伺服器發起請求,由伺服器根據快取標識決定是否使用快取的過程,主要有以下兩種情況:
- 協商快取生效,傳回304和Not Modified
- 協商快取失效,傳回200和請求結果
3.1、Last-Modified和If-Modified-Since
- 瀏覽器首先傳送一個請求,讓服務端在
response essay-header
中傳回請求的資源上次更新時間,就是last-modified
,瀏覽器會快取下這個時間。 - 然後瀏覽器再下次請求中,
request essay-header
中帶上if-modified-since
:[儲存的last-modified的值]
。根據瀏覽器傳送的修改時間和服務端的修改時間進行比對,一致的話代表資源沒有改變,服務端傳回正文為空的響應,讓瀏覽器中快取中讀取資源,這就大大減小了請求的消耗。
由於last-modified依賴的是儲存的絕對時間,還是會出現誤差的情況:
- 儲存的時間是以秒為單位的,1秒內多次修改是無法捕捉到的;
- 各機器讀取到的時間不一致,就有出現誤差的可能性。為了改善這個問題,提出了使用etag。
3.2、ETag和If-None-Match
etag
是http
協議提供的若干機制中的一種Web
快取驗證機制,並且允許客戶端進行快取協商。生成etag常用的方法包括對資源內容使用抗碰撞雜湊函式,使用最近修改的時間戳的雜湊值,甚至只是一個版本號。 和last-modified
一樣.
- 瀏覽器會先傳送一個請求得到
etag
的值,然後再下一次請求在request essay-header
中帶上if-none-match
:[儲存的etag的值]
。 - 透過傳送的
etag
的值和服務端重新生成的etag
的值進行比對,如果一致代表資源沒有改變,服務端傳回正文為空的響應,告訴瀏覽器從快取中讀取資源。
etag能夠解決last-modified的一些缺點,但是etag每次服務端生成都需要進行讀寫操作,而last-modified只需要讀取操作,從這方面來看,etag的消耗是更大的。
二者對比
- 精確度上:
Etag
要優於Last-Modified
。 - 優先順序上:伺服器校驗優先考慮
Etag
。 - 效能上:
Etag
要遜於Last-Modified
4、使用者行為對瀏覽器快取的影響
- 開啟網頁,位址列輸入地址: 查詢
disk cache
中是否有匹配。如有則使用;如沒有則傳送網路請求。 - 普通掃清 (F5):因為 TAB 並沒有關閉,因此
memory cache
是可用的,會被優先使用(如果匹配的話)。其次才是disk cache
。 - 強制掃清 (Ctrl + F5):瀏覽器不使用快取,因此傳送的請求頭部均帶有
Cache-control:no-cache
(為了相容,還帶了Pragma:no-cache
),伺服器直接傳回 200 和最新內容。
5、總結