看了這篇文章,瀏覽器緩存必定能記住

關於瀏覽器緩存方面的知識,也看了好幾篇文章了。你們提到也總能說幾個關鍵詞出來,可是說到如何使用可能又不夠肯定了。所以我在這經過實操記錄下來,方便更好的理解和記憶。前端

首先,常說的瀏覽器緩存的兩種狀況強緩存協商緩存,優先級較高的是強緩存,當強緩存命中失敗時纔會走協商緩存。git

強緩存

強緩存是不須要發送http請求的,當資源命中強緩存時,直接從緩存中獲取,響應狀態返回200,打開控制檯查看Size也不顯示資源大小,而是告訴咱們來自緩存。github

強緩存

強緩存

強緩存如何實現呢,本文後端代碼都經過Koa來演示web

一、Expires

設置過時時間,第一次請求之後響應頭中設置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存在問題,就是客戶端的時間和服務端時間多是不一致的,好比你把電腦時間設置成三天甚至一年後,緩存就無效了。瀏覽器

二、Cache-Control

HTTP1.1新增了Cache-Control字段,Cache-Control優先級高於Expires,二者同時使用時會忽略Expires(Expires保留的做用是向下兼容),Cache-Control字段屬性值比較靈活。緩存

2.1 max-age

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
})
複製代碼

2.2 s-maxage

s-maxagemax-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
})
複製代碼

測試發現圖片沒法緩存

2.3 private和public

和前面兩個屬性相關,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失效。

2.4 no-store和no-cache

no-store比較暴力,不適用任何緩存機制,直接向服務器發起請求,下載完整資源。

no-cache跳過強緩存,也就是Expiresmax-age等都無效了,直接請求服務器,確認資源是否過時,也就是進入協商緩存的階段。協商緩存中有兩個關鍵字段,Last-ModifiedEtag

協商緩存

一、Last-Modified和If-Modified-Since

請看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請求),致使文件更新就沒法檢測了。

二、 ETag和If-None-Match

請看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是基於資源內容的,因此會生成新的值,所以能達到預期效果。

補充:瀏覽器緩存的四個位置

  1. Service Worker Cache
  2. Memory Cache
  3. Disk Cache
  4. Push Cache

Service Worker Cache,這個不少人都聽過,是PWA應用的重要實現機制,推薦資源:PWA應用實戰

Memory CacheDisk Cache就是前面總結的,咱們強緩存和協商緩存存放資源的位置。Memory Cache內存緩存,是效率最高的,固然內存資源也是昂貴的有限的,不可能都使用內存緩存,Disk Cache磁盤緩存,相對來講讀取速度慢些。通常來講,大文件或者內存使用高的狀況下,資源會被丟進磁盤。

Push Cache推送緩存,緩存的最後一道防線。是HTTP2中的內容,須要自行去了解。

總結幾個點

一、協商緩存中有兩組約定的字段,一是Last-ModifiedIf-Modified-Since;二是ETagIf-None-Match。也就是響應頭中存在Last-Modified(或ETag),則下次請求的請求頭中會自動增長If-Modified-Since(或If-None-Match)字段,至因而否走協商緩存取決於具體代碼,好比觸發條件通常就是比較同一組數據是否相同,同時由於第二組更加準確,因此優先級也更高。

二、Cache-Control: privateCache-Control: max-age=0,效果不徹底相同,前者不會讓Expires失效。

三、瀏覽器緩存機制總覽,首先若是命中強緩存就直接使用;不然就進入協商緩存階段,這裏會產生http請求,經過協商緩存的兩組規範,檢查資源是否更新。沒更新的話就返回304狀態碼,不然就從新獲取資源並返回200狀態碼。

四、經過上面的演示能夠看得出來,處理緩存的工做量主要在後端,而後在工做中,靜態資源咱們通常都是直接使用中間件來處理,好比筆者在Koa項目中的話,會用koa-static這個中間件。前端不須要寫相關代碼,後端用現成的輪子,所以緩存相關的知識就被拋棄了。

五、看完這些demo,相信你必定能記住,demo地址,也能夠本身clone下來跑幾遍試試,但願本文對你有幫助。

相關文章
相關標籤/搜索