瀏覽器緩存是大型網站架構(緩存&CDN)重要的考量環節,也是考核前端工程師對HTTP協議理解的重要知識點,下面對瀏覽器緩存機制做個總結。
緩存機制
瀏覽器讀取資源文件的流程如下:
- 瀏覽器發送請求前,根據請求頭的expires和cache-control判斷是否命中(包括是否過期)強緩存策略,如果命中,直接從緩存獲取資源,並不會發送請求。如果沒有命中,則進入下一步;
- 沒有命中強緩存規則,瀏覽器會發送請求,根據請求頭的last-modified和etag判斷是否命中協商緩存,如果命中,直接從緩存獲取資源。如果沒有命中,則進入下一步;
- 如果前兩步都沒有命中,則直接從服務端獲取資源;
邏輯流程圖如下:
強緩存
強緩存即不會向服務器發送請求,直接從緩存中讀取資源。強制緩存的情況主要有三種:
- 不存在緩存結果和緩存標識,直接向服務器發送請求:
- 存在該緩存結果和緩存標識,且該結果尚未失效,強制緩存生效,直接返回該結果:
- 存在緩存標識和緩存結果,但是已經失效,則使用協商緩存:
強緩存的協商規則是通過HTTP響應頭和請求頭來進行的。當瀏覽器向服務器發起請求時,服務器會將緩存規則放入HTTP響應報文的HTTP頭中和請求結果一起返回給瀏覽器,控制強制緩存的字段分別是
Expires和Cache-Control,其中Cache-Control優先級比Expires高。Expires
Expires用來指定資源到期的時間(服務器端的具體的時間點)。Expires=max-age + 請求時間,需要和Last-modified結合使用。Expires是Web服務器響應消息頭字段,在響應http請求時告訴瀏覽器在過期時間前瀏覽器可以直接從瀏覽器緩存取數據,而無需再次請求。
Cache-Control: max-age=93312000
Expires: Mon, 22 Aug 2022 09:32:06 GMT
Expires 是 HTTP/1 的產物,瀏覽器會將本地時間與Expires時間進行判斷,這樣就容易受限於本地時間,如果修改了本地時間,可能會造成緩存失效。
示例:
Cache Control
在HTTP/1.1中,Cache-Control是最重要的規則,主要用於控制網頁緩存。示例:
以上資源請求響應數據中,cache-control的值為max-age=93312000,從字面上面就能夠猜出它的含義,就是服務端告訴瀏覽器
此信息可不可以緩存,以什麼樣的策略進行緩存;cache-control有哪些類型的值呢?- private:瀏覽器可以緩存
- public:瀏覽器和代理服務器可以緩存,因此我們中間層的服務節點,發現cache-control的值為private時,就認可只有發起請求的瀏覽器能夠緩存,作為代理服務節點是不能夠緩存的。如為public時,代理服務器也可以緩存。
- max-age緩存的內容將在xxx秒後失效
- no-store不緩存請求的任何返回內容
- no-cache強制向服務器端再驗證一次,no-cache會緩存請求返回的內容,而no-store不緩存;但no-cache時,在下次用緩存的內容時,需要向服務器驗證一下,緩存到底能不能用
在邏輯流程圖中,有一個環節就是重新驗證,就是
驗證緩存內容是否有效,那驗證邏輯是什麼,怎麼驗證?ETag驗證
ETag是資源唯一標識, 一般會把請求的內容做md5加密,返回唯一的標識;服務器端會把ETag的值一起返回給瀏覽器;瀏覽器會把ETag存儲下來。
ETag: "AFDC802903A35894189B4B1107811066"
瀏覽器下一次請求時將Etag一併帶過去給服務器,服務器只需要比較瀏覽器傳來的ETag跟自己服務器上該資源的ETag是否一致,就能很好地判斷資源相對瀏覽器而言是否被修改過了。如果服務器發現ETag匹配不上,那麼直接以常規GET 200狀態碼形式將新的資源(當然也包括了新的ETag)發給瀏覽器;如果ETag是一致的,則直接返回304狀態碼瀏覽器直接使用本地緩存即可。
那麼瀏覽器是如何把標記在資源上的ETag傳回給服務器的呢?請求報文中有兩個首部字段可以帶上ETag值:
- If-None-Match: ETag-value 告訴服務端如果ETag沒匹配上需要重發資源數據,否則直接回送304和響應報頭即可,當前各瀏覽器均是使用的該請求首部來向服務器傳遞保存的ETag值。
- If-Match: ETag-value 告訴服務器如果沒有匹配到ETag,或者收到了“*”值而當前並沒有該資源實體,則應當返回412(Precondition Failed) 狀態碼給瀏覽器。否則服務器直接忽略該字段。
需要注意的是,如果資源是走分佈式服務器(比如CDN)存儲的情況,需要這些服務器上
計算ETag唯一值的算法保持一致,才不會導致明明同一個文件,在服務器A和服務器B上生成的ETag卻不一樣。ETag可以更加精確的判斷資源是否被修改,可以識別一秒內多次修改的情況;不存在版本問題,每次請求都回去服務器進行校驗。單計算ETag值需要性能損耗,同時分佈式服務器存儲的情況下,計算ETag的算法如果不一樣,會導致瀏覽器從一臺服務器上獲得頁面內容後到另外一臺服務器上進行驗證時發現ETag不匹配的情況。
Last-Modified驗證
服務器將資源傳遞給瀏覽器時,會將資源最後更改的時間以“Last-Modified: GMT”的形式加在實體首部上一起返回給瀏覽器。
Last-Modified: Sat, 07 Sep 2019 09:31:44 GMT
瀏覽器會為資源標記上該信息,下次再次請求時,會把該信息附帶在請求頭中一併帶給服務器去做檢查,若傳遞的時間值與服務器上該資源最終修改時間是一致的,則說明該資源沒有被修改過,直接
返回304狀態碼,內容為空。if-Modified-Since: Sat, 07 Sep 2019 09:31:44 GMT
如果兩個時間不一致,則服務器會發回該資源並返回200狀態碼,和第一次請求時類似。這樣保證不向瀏覽器重複發出資源,也保證當服務器有變化時,瀏覽器能夠得到最新的資源。
閱讀更多 熱愛技術君 的文章