淺談瀏覽器緩存機制

前文

在前端開發中,性能一直都是被你們所重視的一點,然而判斷一個網站的性能最直觀的就是看網頁打開的速度。其中提升網頁反應速度的一個方式就是使用緩存。一個優秀的緩存策略能夠縮短網頁請求資源的距離,減小延遲,而且因爲緩存文件能夠重複利用,還能夠減小帶寬,下降網絡負荷。前端

緩存過程分析

緩存過程分析

由此可知:git

  • 瀏覽器每次發送請求前,都會從瀏覽器緩存中查找緩存及其緩存標識
  • 瀏覽器每次請求到數據後,都會將數據及其緩存標識存入瀏覽器緩存

緩存規則

一、強制緩存階段:先在本地查找該資源,若是有發現該資源,並且該資源尚未過時,就使用這一個資源,徹底不會發送http請求到服務器github

二、協商緩存階段:若是在本地緩存找到對應的資源,可是不知道該資源是否過時或者已通過期,則發一個http請求到服務器,而後服務器判斷這個請求,若是請求的資源在服務器上沒有改動過,則返回304,讓瀏覽器使用本地找到的那個資源;web

三、啓發式緩存階段算法

四、緩存失敗階段:當服務器發現請求的資源已經修改過,或者這是一個新的請求(在原本沒有找到資源),服務器則返回該資源的數據,而且返回200, 固然這個是指找到資源的狀況下,若是服務器上沒有這個資源,則返回404。瀏覽器

緩存標識

一、強制緩存階段

由上面能夠知道強制緩存是直接使用本地的緩存,不發送http請求,那麼判斷它是否要進行強制緩存的標誌是什麼呢?緩存

  • Expires http1.0

Expires是HTTP/1.0控制網頁緩存的字段,其值爲服務器返回該請求結果緩存的到期時間(絕對時間),即再次發起該請求時,若是客戶端的當前時間小於Expires的值時,直接使用緩存結果。服務器

缺點:由於是絕對時間,若是客戶端與服務端的時間由於某些緣由(例如時區不一樣;客戶端和服務端有一方的時間不許確)發生偏差,強緩存可能直接失效。cookie

app.get('/1.jpg',function(req,res,next){
    ...
    res.setHeader('Expires', new Date(Date.now() + 10 * 1000).toUTCString())
  })
複製代碼
  • Cache-Control http1.1

在HTTP/1.1中,Cache-Control是最重要的規則,主要用於控制網頁緩存,它有幾個選項: 一、public : 全部的內容都被緩存(客戶端和代理服務器均可緩存) 二、private : 全部的內容都被緩存(客戶端緩存,cache-control的默認值) 三、no-cache: 客戶端緩存,可是是否使用緩存得經過協商緩存來決定 四、no-store: 全部內容都不緩存,既不強制緩存,又不協商緩存 五、max-age=num(num的單位是秒) : 緩存內容在num秒後失效,num爲相對時間網絡

app.get('/2.jpg',function(req,res,next){
          res.setHeader('Expires', new Date(Date.now() + 10 * 1000).toUTCString())
          res.setHeader('Cache-Control', 'max-age=20')
          ...
  })
複製代碼

通過強緩存後,客戶端再請求,若是緩存標誌有效,則返回灰色200,數據從瀏覽器緩存中取出。 優先級:Cache-Control > expires

二、啓發式緩存階段

當緩存過時時間的字段一個都沒有的時候,瀏覽器下次並不會直接進入協商階段,而是先進入啓發式緩存階段,你能夠經過關閉服務器,刷新頁面來觀察。 它根據響應頭中2個時間字段 Date 和 Last-Modified 之間的時間差值,取其值的10%做爲緩存時間週期。 也就是說,當存有 Last-Modified字段的時候,即便是斷網,且強緩存都失效後,也有必定時間是直接讀取緩存文件的。 etag是沒有這個階段的。

三、協商緩存階段

協商階段就是當強緩存階段失效的時候,http請求會攜帶緩存標誌符向服務器發起請求,由服務器來決定是否使用緩存,瀏覽器根據返回到code碼來決定是否從瀏覽器緩存中拿去數據。

  • Last-Modified / If-Modified-Since http1.0

Last-Modified是服務器響應請求時,返回該資源文件在服務器最後被修改的時間。 If-Modified-Since是再次請求該資源文件的時候,會帶上上次請求中的Last-Modified時間,服務器經過對比Last-Modified / If-Modified-Since,返回200,則有更新,從服務器拉取數據,304則使用緩存文件

app.get('/3.jpg',function(req,res,next){
        ...
        let ifModifiedSince = req.headers['if-modified-since']
        let LastModified = stat.ctime.toGMTString()
        if(!!ifModifiedSince && LastModified === ifModifiedSince){
            res.statusCode = 304
            res.end()
        }
        if(!ifModifiedSince  || (!!ifModifiedSince && LastModified !== ifModifiedSince)){
            res.setHeader('Last-Modified', LastModified)
        }
        ...
    })
})

複製代碼
  • Etag / If-None-Match http1.1

Etag是服務器響應請求時,返回當前資源文件的一個惟一標識(由服務器根據文件信息算法生成,相似hash) If-None-Match是客戶端再次發起該請求時,攜帶上次請求返回的惟一標識Etag值,經過此字段值告訴服務器該資源上次請求返回的惟一標識值。服務器收到該請求後,發現該請求頭中含有If-None-Match,則會根據If-None-Match的字段值與該資源在服務器的Etag值作對比,一致則返回304,表明資源無更新,繼續使用緩存文件;不一致則從新返回資源文件,狀態碼爲200

app.get('/4.jpg',function(req,res,next){
    reponseHandle(req,res,(stat)=>{
        let ifNoneMatch = req.headers['if-none-match']
        let etag = .....
        if(!ifNoneMatch || (!!ifNoneMatch && ifNoneMatch!== etag)){
            res.setHeader('ETag', etag)
        }
        if(!!ifNoneMatch && ifNoneMatch === etag){
            res.statusCode = 304
            res.end()
        }
        
    })
})

複製代碼

Etag / If-None-Match優先級高於Last-Modified / If-Modified-Since,同時存在則只有Etag / If-None-Match生效

因而可知,強制緩存優先於協商緩存進行,若強制緩存(Expires和Cache-Control)生效則直接使用緩存,若不生效則進行協商緩存(Last-Modified / If-Modified-Since和Etag / If-None-Match),協商緩存由服務器決定是否使用緩存,若協商緩存失效,那麼表明該請求的緩存失效,從新獲取請求結果,再存入瀏覽器緩存中;生效則返回304,繼續使用緩存

四、一些常見且須要知道的標識符

字段 說明
pragma http1.0,值爲no-cache爲禁用緩存
vary 基於字段區分緩存版本(res header)
Date 發送響應報文的時間(啓發式緩存、代理服務器緩存)
Age 文件存於服務器的時間
accept-encoding 請求服務器返回的文件類型
referer 發送請求的源域名

服務器緩存

CDN緩存

CDN緩存,也叫網關緩存、反向代理緩存。瀏覽器先向CDN網關發起WEB請求,網關服務器後面對應着一臺或多臺負載均衡源服務器,會根據它們的負載請求,動態地請求轉發到合適的源服務器上。

CDN的優點:

  • CDN節點解決了跨運營商和跨地域訪問的問題,訪問延時大大下降
  • 大部分請求在CDN邊緣節點完成,CDN起到了分流做用,減輕了源站的負載。
  • 經過http響應頭中的Cache-control: max-age的字段來設置CDN邊緣節點數據緩存時間

HTML5緩存

app cache

由於幾個歷史緣由,app cache已經不推薦使用,從web標準移除了。緣由以下:

  • 使用了manifest後,沒辦法清空這些緩存,只能更新緩存,或者得用戶本身去清空瀏覽器的緩存;
  • 假如更新的資源中有一個資源更新失敗了,那麼全部的資源就會所有更新失敗,將用回上一版本的緩存;
  • 主頁會被強制緩存(使用了manifest的頁面),而且沒法清除;
  • appache文件可能會沒法被及時更新,由於各大瀏覽器對於appcache文件的處理方式不一樣;

service worker

Service workers 本質上充當Web應用程序與瀏覽器之間的代理服務器,也能夠在網絡可用時做爲瀏覽器和網絡間的代理。它們旨在(除其餘以外)使得可以建立有效的離線體驗,攔截網絡請求並基於網絡是否可用以及更新的資源是否駐留在服務器上來採起適當的動做。他們還容許訪問推送通知和後臺同步API。

service worker的特色:

  • 要求 HTTPS 環境,開發過程當中,通常瀏覽器也容許 host 爲 localhost 或者 127.0.0.1
  • 運行在它本身的全局腳本上下文中
  • 不綁定到具體的網頁,沒法修改網頁中的元素,由於它沒法訪問 DOM
  • 一旦被 install,就永遠存在,除非被手動 unregister
  • 異步實現,內部大都是經過 Promise 實現,依賴 HTML5 fetch API
  • Service Worker 的緩存機制是依賴 Cache API 實現的

具體請移步

Cookie

  • Cookie大小在4k左右
  • http請求會默認帶上
  • 域名、端口相同便可共享(CSRF)
  • 通常不直接使用,會進行簡單的封裝,請移步
  • Cookie的常見屬性
key value
expires Cookie失效時間(絕對時間)若不設置,瀏覽器關閉即刪除
Max-Age Cookie失效時間,相對時間,優先級高於expires
path 設置哪些路徑帶上cookie,通常默認爲'/'
Domain 設置哪些域名帶上cookie,通常爲當前一級域名
Secure 只在https下才能發送cookie
HttpOnly js腳本獲取不到,且只能在http上才能發送cookie(防止xss)
SameSite 是否可能做爲第三方 cookie(防止CSRF)

localStorage、sessionStorage

相同點

  • 存儲大小都在5M左右
  • 會返回一個bol來標識是否存儲成功
  • 同源策略限制
  • 不能直接和服務器通訊,能夠經過腳本注入請求參數裏面

不一樣點

  • localStorage存儲數據永久性直到人爲刪除、sessionStorage和窗口的生命週期相同
  • localStorage知足同源策略能夠跨窗口存儲,sessionStorage不行

最後

推薦一下本身的我的公衆號:前端精讀(每日定時推送一篇前端好文)

前端每日精讀
相關文章
相關標籤/搜索