你真的懂 HTTP 緩存嗎

作者:程序員小楊 v1

原文:https://juejin.cn/post/7281088405189427215

背景

需求開發中不斷的往項目中添加圖片、字體等這些靜態資源使得項目打包體積越來越大。

打包後這些靜態資源佔據了包體積的大部分。

基於此,我們準備將靜態資源從項目中移出來放到 oss 雲服務上,這樣項目的體積會縮小很多,打包速度也會快很多

但是,想法是好的,但是具體的操作上存在一些問題需要解決:

這篇文章我會重點分享靜態資源緩存問題的解決方案。

HTTP 緩存

說到靜態資源的緩存問題其實就是 HTTP 緩存的問題,我們既要保證客戶端能快速的加載靜態資源還需要保證當靜態資源變化時,客戶端能及時更新。

緩存過程

由上圖我們可以發現:

以上兩點是瀏覽器緩存機制的關鍵,它確保了每個請求的緩存存入與讀取。

根據是否需要向服務器重新發起 HTTP 請求將緩存過程分爲兩個部分,分別是強緩存和協商緩存。

強緩存

強緩存,顧名思義就是強制緩存,是不需要向原服務器發起請求直接使用瀏覽器緩存

強緩存的實現方案主要是有兩種:Expires 和 cache-control

Expires 和 cache-control 都可以實現強緩存

資源在 2023 年 2 月 28 號 22:22:22 過期

Expires: Tue, 28 Feb 2023 22:22:22 GMT

資源在 1 小時內可使用緩存

Date: Tue, 22 Feb 2022 22:22:22 GMT
Last-Modified: Tue, 22 Feb 2022 22:00:00 GMT
Cache-Control: max-age=3600

但是Expires 是 http1.0 的產物,cache-control是 http1.1 的產物,現階段,我們會認爲cache-control使用更廣泛,Expires只是一種降級方案。

Expires 和 cache-control同時存在時,瀏覽器會優先使用cache-control

協商緩存

過程:當強緩存過期,瀏覽器會啓用協商緩存,首先判斷當前是否存在ETag,如果存在,則再次向服務器發送請求時,在請求頭上加上If-None-Match一同發送到服務器,服務器比對當前的If-None-Match與資源相對應的ETag是否相同,如果相同則說明緩存可用,則返回 304,否則返回最新的資源。微信搜索公衆號:架構師指南,回覆:架構師 領取資料 。

過程:當資源過期時,如果不存在ETag而是存在Last-modified,則再次向服務器發送請求時,在請求頭上會帶上If-Modified-Since一同發送給服務器,服務器對比資源最後修改時間和當前客戶端發送的If-Modified-Since這個時間,判斷資源是否被修改,如果修改了則返回最新的資源,如果沒有修改則返回 304,使用緩存。

ETag的優先級高於Last-Modified, 服務器會優先驗證ETag

Last-Modified是以秒爲單位的,所以在資源頻繁更改的情況下,Last-Modified是不安全的,而ETag可以檢查文件大小和文件的唯一索引節點,故即使修改頻繁的資源依然能檢測到更改

緩存策略

基於上面介紹的強緩存和協商緩存特點,以下是筆者總結的緩存策略;

頻繁變動的資源

對於頻繁變動的資源,肯定首先要保證的就是及時性,即服務器文件更新,需要儘快展示在客戶端,所以對於此種資源,建議禁用強制緩存,採用協商緩存的策略。

cache-control: no-cache
Last-Modified: Tue, 22 Feb 2022 22:00:00 GMT
ETag: deadbeef

以上標識中

過程如下:

  1. 當瀏覽器查發現cache-control: no-cache 時,會向服務器發起該資源的請求

  2. 但是在請求前,瀏覽器會先在自己的緩存中查找該資源, 如果發現存在該資源的緩存文件,則將該緩存文件的EtagLast-Modified字段轉換爲請求頭中的If-None-MatchIf-Modified-Since一同發送到服務端

  3. 如果發現不存在該資源,則直接請求

  4. 服務器接收到資源請求,如果不存在If-None-MatchIf-Modified-Since請求頭,則說明客戶端沒有緩存,直接返回資源

  5. 如果存在If-None-MatchIf-Modified-Since請求頭,服務器比對當前的If-None-Match與資源相對應的ETag是否相同,如果相同則說明文件沒有更改,返回 304 讓瀏覽器使用緩存,如果不相同,返回新文件。

不經常變化的資源

對於不經常變化的資源,建議設置強緩存,

cache-control:max-age=31536000

如上設置會強緩存該資源一年時間,如果我們中間需要修改該資源,可以採用在資源鏈接末尾添加唯一 hash 或是唯一時間戳來解決。

加餐

對於需要緩存的文件,建議明確指出cache-control字段,如果沒有設置該字段,則瀏覽器會採用一種叫做啓發式緩存的方式緩存文件。

HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1024
Date: Tue, 22 Feb 2022 22:22:22 GMT
Last-Modified: Tue, 22 Feb 2021 22:22:22 GMT

如果存在以上響應,不存在cache-control字段

試探性地知道,整整一年沒有更新的內容在那之後的一段時間內不會更新。

因此,客戶端存儲此響應(儘管缺少 max-age)並重用它一段時間。

複用多長時間取決於實現,但規範建議存儲後大約 10%(在本例中爲 0.1 年)的時間。

由此可見,如果不指明cache-control字段會導致不必要的緩存,或是無謂的流量消耗。

所以,建議所有靜態資源都明確指出cache-control字段,明確緩存策略。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/EnenRdgPiDe3tXzr504PYQ