關於瀏覽器緩存方面的知識,也看了好幾篇文章了。你們提到也總能說幾個關鍵詞出來,可是說到如何使用可能又不夠肯定了。所以我在這經過實操記錄下來,方便更好的理解和記憶。前端
首先,常說的瀏覽器緩存的兩種狀況強緩存
和協商緩存
,優先級較高的是強緩存,當強緩存命中失敗時纔會走協商緩存。git
強緩存是不須要發送http請求的,當資源命中強緩存時,直接從緩存中獲取,響應狀態返回200,打開控制檯查看Size
也不顯示資源大小,而是告訴咱們來自緩存。github
強緩存如何實現呢,本文後端代碼都經過Koa來演示web
設置過時時間,第一次請求之後響應頭中設置Expires
後端
router.get('/img/1.jpg', ctx => {
const file = fs.readFileSync(path.resolve(__dirname,`.${ctx.request.path}`))
ctx.set({
'Expires': new Date((+new Date() + 1000*60*60*24*3))
})
ctx.body = file
})
複製代碼
將過時時間設置爲三天後,這樣在緩存未過時下就不須要再從新請求服務端了。可是Expires
存在問題,就是客戶端的時間和服務端時間多是不一致的,好比你把電腦時間設置成三天甚至一年後,緩存就無效了。瀏覽器
HTTP1.1新增了Cache-Control
字段,Cache-Control優先級高於Expires,二者同時使用時會忽略Expires(Expires保留的做用是向下兼容),Cache-Control字段屬性值比較靈活。緩存
max-age指定資源的有效時間,單位是秒。如下是demo2,Cache-controla:max-age=0
,同時設置Expires。測試能夠看到圖片始終不會緩存,也體現出了優先級的問題。服務器
// demo2
router.get('/img/2.jpg', ctx => {
const file = fs.readFileSync(path.resolve(__dirname,`.${ctx.request.path}`))
ctx.set({
'Expires': new Date((+new Date() + 1000*60*60*24*3)),
'Cache-control': 'max-age=0'
})
ctx.body = file
})
複製代碼
再測試demo3,這樣圖片的緩存時間就是10秒koa
// demo3
router.get('/img/3.jpg', ctx => {
const file = fs.readFileSync(path.resolve(__dirname,`.${ctx.request.path}`))
ctx.set({
'Cache-control': 'max-age=10'
})
ctx.body = file
})
複製代碼
s-maxage
和max-age
相似,不一樣的地方s-maxage
是針對代理服務器的測試
// demo4
router.get('/img/4.jpg', ctx => {
const file = fs.readFileSync(path.resolve(__dirname,`.${ctx.request.path}`))
ctx.set({
'Expires': new Date((+new Date() - 1000*60*60*24*3)),
'Cache-control': 's-maxage=10'
})
ctx.body = file
})
複製代碼
測試發現圖片沒法緩存
和前面兩個屬性相關,private
對應資源能夠被瀏覽器緩存,public
表示資源既能夠被瀏覽器緩存,也能夠被代理服務器緩存。 默認值是private
,至關於設置了max-age
的狀況;當設置了s-maxage
屬性,就表示能夠被代理服務器緩存,也就是等同於設置成public
。
這裏請看demo5
// demo5
router.get('/img/5.jpg', ctx => {
const file = fs.readFileSync(path.resolve(__dirname,`.${ctx.request.path}`))
ctx.set({
'Cache-control': 'private'
})
ctx.body = file
})
複製代碼
測試發現圖片始終不會緩存,因此private
對應max-age
的默認值應該是0。可是private真的和max-age=0徹底相同嗎,我又寫了個栗子測試。
//demo6
router.get('/img/6.jpg', ctx => {
const file = fs.readFileSync(path.resolve(__dirname,`.${ctx.request.path}`))
ctx.set({
'Expires': new Date((+new Date() + 1000*60*60*24*3)),
'Cache-control': 'private'
})
ctx.body = file
})
複製代碼
測試發現圖片會被緩存,說明private
狀況下,不會讓Expires
失效。
no-store
比較暴力,不適用任何緩存機制,直接向服務器發起請求,下載完整資源。
no-cache
跳過強緩存,也就是Expires
、max-age
等都無效了,直接請求服務器,確認資源是否過時,也就是進入協商緩存的階段。協商緩存中有兩個關鍵字段,Last-Modified
和Etag
請看demo8
// demo8
router.get('/img/8.jpg', ctx => {
const file = fs.readFileSync(path.resolve(__dirname,`.${ctx.request.path}`))
const stats = fs.statSync(path.resolve(__dirname,`.${ctx.request.path}`))
if(ctx.request.header['if-modified-since'] === stats.mtime.toUTCString()){
return ctx.status = 304
}
ctx.set({
'Cache-control': 'no-cache',
'Last-Modified': stats.mtime.toUTCString()
})
ctx.body = file
})
複製代碼
第一次請求後,在響應頭中加入Last-Modified
字段,返回資源的最後修改時間,下次請求時客戶端請求頭會帶上If-Modified-Since
的字段,裏面的值就是以前響應頭中Last-Modified
的值,而後進行比較,若是沒有變化,則返回304的狀態碼。
若是改造一下把'Cache-control': 'no-cache'
去掉呢,測試後發現,在不清緩存的狀況資源就變成強緩存了,請求頭中的If-Modified-Since
也沒有了(加上也沒用,由於不會發送http請求),致使文件更新就沒法檢測了。
請看demo9
// demo9
router.get('/img/9.jpg', ctx => {
const file = fs.readFileSync(path.resolve(__dirname,`.${ctx.request.path}`))
const stats = fs.statSync(path.resolve(__dirname,`.${ctx.request.path}`))
if(ctx.request.header['if-none-match']){
if(ctx.request.header['if-none-match'] === 'abc123'){
return ctx.status = 304
}
}else if(ctx.request.header['if-modified-since'] === stats.mtime.toUTCString()){
return ctx.status = 304
}
ctx.set({
'Cache-control': 'no-cache',
'Last-Modified': stats.mtime.toUTCString(),
'ETag': 'abc123'
})
ctx.body = file
})
複製代碼
和Last-Modified
相似,第一次請求之後,響應頭中會增長ETag
字段,ETag
經過資源的內容生成一個標識符,下次請求在請求中增長If-None-Match
字段,值就是以前響應頭中ETag
的值,而後進行比較。ETag
能夠說是對Last-Modified
的一個補充,由於Last-Modified
也是有不足的地方。舉個栗子,Last-Modified中的時間是精確到秒的,若是同一秒內文件被修改了一次,下一次請求時,預期獲取新資源而實際仍是會走協商緩存。而ETag
是基於資源內容的,因此會生成新的值,所以能達到預期效果。
Service Worker Cache
,這個不少人都聽過,是PWA應用的重要實現機制,推薦資源:PWA應用實戰。
Memory Cache
和Disk Cache
就是前面總結的,咱們強緩存和協商緩存存放資源的位置。Memory Cache
內存緩存,是效率最高的,固然內存資源也是昂貴的有限的,不可能都使用內存緩存,Disk Cache
磁盤緩存,相對來講讀取速度慢些。通常來講,大文件或者內存使用高的狀況下,資源會被丟進磁盤。
Push Cache
推送緩存,緩存的最後一道防線。是HTTP2中的內容,須要自行去了解。
一、協商緩存中有兩組約定的字段,一是Last-Modified
和If-Modified-Since
;二是ETag
和If-None-Match
。也就是響應頭中存在Last-Modified
(或ETag
),則下次請求的請求頭中會自動增長If-Modified-Since
(或If-None-Match
)字段,至因而否走協商緩存取決於具體代碼,好比觸發條件通常就是比較同一組數據是否相同,同時由於第二組更加準確,因此優先級也更高。
二、Cache-Control: private
和Cache-Control: max-age=0
,效果不徹底相同,前者不會讓Expires
失效。
三、瀏覽器緩存機制總覽,首先若是命中強緩存就直接使用;不然就進入協商緩存階段,這裏會產生http請求,經過協商緩存的兩組規範,檢查資源是否更新。沒更新的話就返回304狀態碼,不然就從新獲取資源並返回200狀態碼。
四、經過上面的演示能夠看得出來,處理緩存的工做量主要在後端,而後在工做中,靜態資源咱們通常都是直接使用中間件來處理,好比筆者在Koa項目中的話,會用koa-static
這個中間件。前端不須要寫相關代碼,後端用現成的輪子,所以緩存相關的知識就被拋棄了。
五、看完這些demo,相信你必定能記住,demo地址,也能夠本身clone下來跑幾遍試試,但願本文對你有幫助。