緩存相關(guān)header
- Expires
響應(yīng)頭,代表資源的過期時間
- Cache-Control
請求/響應(yīng)頭,緩存控制字段,精確控制緩存策略
- If-modified-Since
請求頭,資源最近修改時間,由瀏覽器告訴服務(wù)器
- Last-Modified
響應(yīng)頭,資源最近修改時間,由服務(wù)器告訴瀏覽器
- Etag
響應(yīng)頭,資源標識,由服務(wù)器告訴瀏覽器
- If-None-Match
請求頭,緩存資源標識,由瀏覽器告訴服務(wù)器
配對使用的字段
- If-modified-Since和Last-Modified
- Etag和If-None-Match
從實際場景出發(fā),一點點完善緩存機制,來理解各個字段的意義
做一些約定,方便以后比較。
- a.js 大小為10KB
- 請求頭約定為1KB
- 響應(yīng)頭約定為1KB
原始模型
- 瀏覽器請求靜態(tài)資源a.js(請求頭1KB)
- 服務(wù)器讀取磁盤文件,返回給瀏覽器。(文件+響應(yīng)頭 11KB)
- 瀏覽器再次請求,服務(wù)器又重新讀取文件,返回給瀏覽器
消耗的流量與訪問次數(shù)正相關(guān)
瀏覽增加緩存機制
- 瀏覽器第一次請求a.js,緩存a.js到本地磁盤 (1+10+1= 12KB)
- 瀏覽器再次請求a.js,直接走瀏覽器緩存,不再向服務(wù)器發(fā)起請求 (0KB)
流量與訪問次數(shù)無關(guān),只有第一次請求會消耗瀏覽
缺點:服務(wù)器a.js更新后,瀏覽器也拿不到最新的資源
服務(wù)器和瀏覽器約定資源過期時間 Expires
- 瀏覽器第一次請求靜態(tài)資源a.js(1KB)
- 服務(wù)器把文件和文件緩存過期時間發(fā)給瀏覽器
- 瀏覽器收到文件,記住了過期時間
- 在過期時間之前,瀏覽器再次請求a.js,會直接使用上一次緩存的文件
- 在過期時間之后,再次請求,會去請求服務(wù)器,服務(wù)器重新讀取文件返回給瀏覽器,同時告知瀏覽器新的過期時間。
缺點:緩存過期之后,服務(wù)器不管a.js有無變化,都會再次讀取a.js返回給瀏覽器
服務(wù)器告訴瀏覽器資源上次修改時間
- 瀏覽器訪問a.js
- 服務(wù)器返回a.js時,告訴瀏覽器a.js的上次修改時間Last-Modified及緩存過期時間Expires
- 當過期時間之后請求a.js,會帶上 If-Modified-Since(等于上一次請求的Last-Modified)
- 服務(wù)器比較請求頭里的If-Modified-Since和文件的上次修改時間
- 如果一致,則告知瀏覽器可以繼續(xù)使用本地緩存(304)
- 如果不一致,則讀取文件返回給瀏覽器,并帶上新的 Last-Modified及緩存過期時間Expires
缺點:Expires 過期控制不穩(wěn)定;Last-Modified 過期時間只能精確到秒(一秒內(nèi)文件變化頻繁,獲取不到最新的文件;可能存在文件未變化,但修改時間更新了的情況)。
增加相對時間的控制Cache-Contorl
為了兼容已有緩存方案,同時加入新的緩存方案。除了告訴瀏覽器Expires,還告訴一個相對時間 Cache-Control:max-age=10秒,含義是10秒內(nèi)使用緩存到瀏覽器的a.js。
瀏覽器先檢查Cache-Control,如果有,則以Cache-Control為準,忽略Expires。如果沒有Cache-Control則以Expires為準。
Cache-Control的常用設(shè)置值:
- no-cache:不使用本地緩存,使用協(xié)商緩存
- no-store:直接禁止瀏覽器緩存數(shù)據(jù),每次用戶請求該資源,都會向服務(wù)器發(fā)送請求,服務(wù)器都重新下載資源
- public:可以被所有用戶緩存,包括終端用戶和CDN等中間代理服務(wù)器
- private:只能被終端用戶的瀏覽器緩存,不允許中繼緩存服務(wù)器對其緩存
增加文件內(nèi)容對比,ETag 和 If-None-Match
a.js 內(nèi)容變了,Etag 才變。內(nèi)容不變,Etag 不變,可以理解為 Etag 是文件內(nèi)容的唯一 ID。
每次瀏覽器請求服務(wù)器的時候,都帶上If-None-Match字段,該字段的值就是上次請求 a.js 時,服務(wù)器返回給瀏覽器的 Etag。
- 瀏覽器請求a.js.
- 服務(wù)器返回a.js,同時告訴瀏覽器過期絕對時間Expires和相對時間Cache-Control:max-age=10),文件上次修改時間Last-Modified以及文件Etag。
- 10秒內(nèi),瀏覽器再次請求,直接用本地緩存。
- 10秒后,瀏覽器再次請求,帶上上次修改時間 If-Modified-Since和If-None-Match。
- 服務(wù)器收到請求,發(fā)現(xiàn)有If-Modified-Since和If-None-Match。存在If-None-Match,則忽略If-Modified-Since。如果If-None-Match的值跟文件的Etag值相同,則告訴瀏覽器繼續(xù)使用本地緩存,否則返回新的文件及其他字段信息。
還存在的問題
無論是Expires還是Cache-Control,在緩存過期之前,瀏覽器都不會發(fā)請求詢問服務(wù)器,如果在這期間,服務(wù)器里的文件發(fā)生了變化,瀏覽器是感知不到的。
可以想象一下我們使用a.js的場景,一般都是輸入網(wǎng)址,訪問一個html文件,html文件里引入js、css、圖片等資源。
所以我們可以設(shè)置讓html文件不緩存,每次都拿到最新的html。然后如果js文件內(nèi)容有更新時,更新引用js的地方,可以通過版本號來區(qū)分,也可以通過hash值區(qū)分。使用webpack打包的話,借助插件可以很方便的處理。
<script src="http://test.com/a.js?version=0.0.1"></script>
<script src="http://test.com/a.【hash值】.js"></script>
強緩存和協(xié)商緩存
強緩存是利用Expires和Cache-Control兩個字段來控制的。
協(xié)商緩存是由服務(wù)器來確定緩存資源是否可用,主要通過Etag和If-None-Match、Last-Modified和If-Modified-Since這兩組字段來控制。