什麼是http快取呢,當我們使用chrome瀏覽器,按F12開啟控制檯,在網路請求中有時候看到狀態碼是200,有時候狀態碼是304,當我們去看這種請求的時候,我們會發現狀態碼為304的狀態結果是:Status Code: 304 Not Modified,而狀態碼為200的時候一般會有四種情況,一種是直接返回200,沒有任何其他的標誌,另一種是Status Code: 200 OK (from memory cache),還有一種是Status Code: 200 (from disk cache)。最後一種不是太常見,Status Code: 200 (from Service Worker).後面這三種狀態碼看到的效果是灰色的,其實從給出的資訊也能看出來是從快取中獲取上資料。下面我們來詳細介紹一下他們都分別是什麼時候出現的。
其實我們可以按狀態碼來區分其為兩大類,分別是協商快取--304和強制快取--200
協商快取(304)
這種方式使用到了headers請求頭裡的兩個欄位,Last-Modified & If-Modified-Since 。伺服器通過響應頭Last-Modified告知瀏覽器,資源最後被修改的時間:
Last-Modified: Thu, 20 Jun 2019 15:58:05 GMT
當再次請求該資源時,瀏覽器需要再次向伺服器確認,資源是否過期,其中的憑證就是請求頭If-Modified-Since欄位,值為上次請求中響應頭Last-Modified欄位的值:
If-Modified-Since: Thu, 20 Jun 2019 15:58:05 GMT
瀏覽器在傳送請求的時候伺服器會檢查請求頭request header裡面的If-modified-Since,如果最後修改時間相同則返回304,否則給返回頭(response header)新增last-Modified並且返回資料(response body)。
另外,瀏覽器在傳送請求的時候伺服器會檢查請求頭(request header)裡面的if-none-match的值與當前檔案的內容通過hash演算法(例如 nodejs: cryto.createHash('sha1'))生成的內容摘要字元對比,相同則直接返回304,否則給返回頭(response header)新增etag屬性為當前的內容摘要字元,並且返回內容。
綜上總結為:
- 請求頭last-modified的日期與響應頭的last-modified一致
- 請求頭if-none-match的hash與響應頭的etag一致
這兩種情況會返回Status Code: 304
強制快取(200(from ...))
這裡我們使用到了Cache-Control和Expires這兩個欄位來進行控制,Cache-Control裡面可以有多個屬性,可以組合設定,其屬性有如下幾種:
- max-age=xxx,最大的有效時間 單位秒,是一個相對時間
- must-revalidate,如果超過了max-age的時間,必須向伺服器傳送請求,驗證資源的有效性
- no-cache,基本等價於max-age=0,由協商快取來決定是否快取資源
- no-store,真正意義上的不快取
- public,代表 http 請求返回的內容所經過的任何路徑當中(包括中間一些http代理伺服器以及發出請求的客戶端瀏覽器),都可以對返回內容進行快取操作
- private,代表只有發起請求的瀏覽器才可以進行快取。預設值
比如我們設定
Catch-Control:public,max-age=360000
我們在之前說了強制快取有三種情況,上面說返回200有四種,第一種其實是不快取,正常伺服器返回響應。
Service Worker
這個東西其實本質上時伺服器和客戶端之間的代理伺服器,一般我們在使用react開發的時候,會發現在根目錄出現了一個server-worker.js檔案,這個東西就是和service Worker快取相關的,他會根據網路的狀態做出不同的快取策略,有時候斷網了,之前訪問過的介面有可能依然會返回資料,其資料來源就是從其快取中讀取。
memory cache
這個是將資源快取在了記憶體中。事實上,所有的網路請求都會被瀏覽器快取到記憶體中,當然,記憶體容量有限,快取不能無限存放在記憶體中,因此,註定是個短期快取。而其控制權在於瀏覽器。
disk cache
與記憶體快取相對的,這個是將資源快取在硬碟中。雖然相比於記憶體,硬碟的讀取速度要慢很多,但總比沒有強。硬碟快取的控制權在後端,通過什麼控制呢?通過HTTP響應頭控制,也就是我們在上面說到的catche-control和expires
Expires設定的過期時間是一個絕對的GMT時間,例如:Expires:Thu,20 Jun 2019 20:00:00 GMT; 他告訴瀏覽器快取有效性持續到2019年6月20日為止,一直都使用快取來處理。
Expires有一個非常大的缺陷,它使用一個固定的時間,要求伺服器與客戶端的時鐘保持嚴格的同步,並且這一天到來後,伺服器還得重新設定新的時間。
HTTP1.1引入了Cathe-Control,它使用max-age指定元件被快取多久,從請求開始在max-age時間內瀏覽器使用快取,之外的使用請求,這樣就可以消除Expires的限制,
如果對瀏覽器相容性要求很高的話,可以兩個都使用。
其實在上面說到的Last-Modified對比最後修改時間與Expires一樣是有缺陷的,如果,資源的變化的時間間隔小於秒級,比如說是毫秒級的,或者說資源直接是動態生成的,那根據Last-Modified判斷,資源就是每時每刻都最新的,即被修改過!
所以,Etag & If-Node-Match 就是來解決這個問題的。
Etag欄位的值為檔案的特殊標識,一般都是hash生成的,伺服器儲存著資源的Etag值。接下來的流程都與Lst-Modified & If-Modified-Since一致,只不過,比較的值從最後修改時間變成了Etag值。
Etag的優點在於,對於動態資源或者現在流行的Restful API返回的JSON資料,這些是沒有修改時間這一說法的,但是Http標準並沒有規定Etag值如何生成,因此我們通過程式碼自己生成Etag值。當然,計算Etag值會消耗伺服器效能。
Cache-Control+Last-Modified+ETag 的優先順序會如何?
因為http1.1>http1.0,所以Cache-Control>Expires,ETag>Last-Modified。依照就近原則,先找本地快取,沒有再向伺服器發請求,所以Expires>Last-Modified,Cache-Control>ETag,
如果瀏覽器只支援http1.0,那麼瀏覽器只會攜帶Last-Modified傳送給後臺,
如果伺服器只支援http1.0,那麼伺服器會以Last-Modified為標準。
如果瀏覽器支援http1.1,那麼瀏覽器會攜帶Cache-Control+Last-Modified+ETag傳送給後臺,
如果伺服器支援http1.1,那麼伺服器會以Cache-Control+ETag為標準。
200狀態碼和304狀態碼何時出現
在沒有設定Cache-Contral的情況下,設定Last-Modified和ETag快取,會出現200(from cache)和304 交替出現的情況。
設定Cache-Contral的情況下,過期重新整理會出現304(如果有更新內容,則是200),之後再過期之前重新整理都是200(from cache)。如果要確保要向服務端確認,可以將Cache-Contral的max-age設定為0。
通過下圖我們可以清晰的明白200和304
https://www.oecom.cn/http-header-control/