瀏覽器緩存,想說愛你不容易

  今天小微開店寶在測試環境發佈更新的時候,同事問:「爲何我須要手動清理瀏覽器緩存才能看到變動?難道系統上線後也須要客戶本身清理瀏覽器緩存嗎!」看來,這個坑須要我來填了。前端

什麼是瀏覽器緩存

瀏覽器緩存(Brower Caching)是瀏覽器在本地磁盤對用戶最近請求過的文檔進行存儲,當訪問者再次訪問同一頁面時,瀏覽器就能夠直接從本地磁盤加載文檔。web

瀏覽器緩存的優勢有:面試

  1. 減小了冗餘的數據傳輸,節省了網費
  2. 減小了服務器的負擔,大大提高了網站的性能
  3. 加快了客戶端加載網頁的速度

在前端開發面試中,瀏覽器緩存是web性能優化面試題中很重要的一個知識點,從而說明瀏覽器緩存是提高web性能的一大利器,可是瀏覽器緩存若是使用不當,也會產生不少問題,正所謂是,想說愛你,並非很容易的事。因此,結合最近遇到的案例,本文對瀏覽器緩存相關的知識進行總結概括,但願對讀者有所幫助。瀏覽器

瀏覽器緩存的分類

瀏覽器緩存主要有兩類:緩存協商完全緩存,也有稱之爲協商緩存強緩存緩存

瀏覽器在第一次請求發生後,再次請求時:性能優化

  1. 瀏覽器會先獲取該資源緩存的header信息,根據其中的expirescahe-control判斷是否命中強緩存,若命中則直接從緩存中獲取資源,包括緩存的header信息,本次請求不會與服務器進行通訊;
  2. 若是沒有命中強緩存,瀏覽器會發送請求到服務器,該請求會攜帶第一次請求返回的有關緩存的header字段信息(Last-Modified/IF-Modified-Since、Etag/IF-None-Match),由服務器根據請求中的相關header信息來對比結果是否命中協商緩存,若命中,則服務器返回新的響應header信息更新緩存中的對應header信息,可是並不返回資源內容,它會告知瀏覽器能夠直接從緩存獲取;不然返回最新的資源內容

強緩存

強緩存是利用http的返回頭中的Expires或者Cache-Control兩個字段來控制的,用來表示資源的緩存時間。服務器

Expires
該字段是http1.0時的規範,它的值爲一個絕對時間的GMT格式的時間字符串,好比Expires:Mon,18 Oct 2066 23:59:59 GMT。這個時間表明着這個資源的失效時間,在此時間以前,即命中緩存。這種方式有一個明顯的缺點,因爲失效時間是一個絕對時間,因此當服務器與客戶端時間誤差較大時,就會致使緩存混亂。性能

Cache-Control
Cache-Control是http1.1時出現的header信息,主要是利用該字段的max-age值來進行判斷,它是一個相對時間,例如Cache-Control:max-age=3600,表明着資源的有效期是3600秒。cache-control除了該字段外,還有下面幾個比較經常使用的設置值:測試

  • no-cache:不使用本地緩存。須要使用緩存協商,先與服務器確認返回的響應是否被更改,若是以前的響應中存在ETag,那麼請求的時候會與服務端驗證,若是資源未被更改,則能夠避免從新下載。
  • no-store:直接禁止遊覽器緩存數據,每次用戶請求該資源,都會向服務器發送一個請求,每次都會下載完整的資源。
  • public:能夠被全部的用戶緩存,包括終端用戶和CDN等中間代理服務器。
  • private:只能被終端用戶的瀏覽器緩存,不容許CDN等中繼緩存服務器對其緩存。

Cache-Control與Expires能夠在服務端配置同時啓用,同時啓用的時候Cache-Control優先級高。優化

協商緩存

協商緩存就是由服務器來肯定緩存資源是否可用,因此客戶端與服務器端要經過某種標識來進行通訊,從而讓服務器判斷請求資源是否能夠緩存訪問,這主要涉及到下面兩組header字段,這兩組搭檔都是成對出現的,即第一次請求的響應頭帶上某個字段(Last-Modified或者Etag),則後續請求則會帶上對應的請求字段(If-Modified-Since或者If-None-Match),若響應頭沒有Last-Modified或者Etag字段,則請求頭也不會有對應的字段。

Last-Modify/If-Modify-Since

瀏覽器第一次請求一個資源的時候,服務器返回的header中會加上Last-Modify,Last-modify是一個時間標識該資源的最後修改時間,例如Last-Modify: Thu,31 Dec 2037 23:59:59 GMT。

當瀏覽器再次請求該資源時,request的請求頭中會包含If-Modify-Since,該值爲緩存以前返回的Last-Modify。服務器收到If-Modify-Since後,根據資源的最後修改時間判斷是否命中緩存。

若是命中緩存,則返回304,而且不會返回資源內容,而且不會返回Last-Modify。

ETag/If-None-Match

與Last-Modify/If-Modify-Since不一樣的是,Etag/If-None-Match返回的是一個校驗碼。ETag能夠保證每個資源是惟一的,資源變化都會致使ETag變化。服務器根據瀏覽器上送的If-None-Match值來判斷是否命中緩存。

與Last-Modified不同的是,當服務器返回304 Not Modified的響應時,因爲ETag從新生成過,response header中還會把這個ETag返回,即便這個ETag跟以前的沒有變化。

爲何要有Etag

你可能會以爲使用Last-Modified已經足以讓瀏覽器知道本地的緩存副本是否足夠新,爲何還須要Etag呢?HTTP1.1中Etag的出現主要是爲了解決幾個Last-Modified比較難解決的問題:

  • 一些文件也許會週期性的更改,可是他的內容並不改變(僅僅改變的修改時間),這個時候咱們並不但願客戶端認爲這個文件被修改了,而從新GET;
  • 某些文件修改很是頻繁,好比在秒如下的時間內進行修改,(比方說1s內修改了N次),If-Modified-Since能檢查到的粒度是s級的,這種修改沒法判斷(或者說UNIX記錄MTIME只能精確到秒);
  • 某些服務器不能精確的獲得文件的最後修改時間。

Last-Modified與ETag是能夠一塊兒使用的,服務器會優先驗證ETag,一致的狀況下,纔會繼續比對Last-Modified,最後才決定是否返回304。

強緩存與協商緩存的區別能夠用下表來表示:
 |獲取資源形式|狀態碼|發送請求到服務器
------|------------|------|----------------
強緩存|從緩存取 |200(from cache)|否,直接從緩存取
協商緩存|從緩存取|304(Not Modified)|否,經過服務器來告知緩存是否可用

用戶行爲對緩存的影響

用戶操做 Expires/Cache-Control Last-Modied/Etag
地址欄回車 有效 有效
頁面連接跳轉 有效 有效
新開窗口 有效 有效
前進回退 有效 有效
F5刷新 無效 有效
Ctrl+F5強制刷新 無效 無效

實際問題分析

如文章開頭所屬,代碼更新到線上後用戶瀏覽器不能自行更新,咱們不能要求客戶在系統更新後都進行一次緩存清理的操做。

到底該如何解決呢?

在資源請求的URL中增長一個參數,好比:js/mian.js?ver=0.7.1。這個參數是一個版本號,每一次部署的時候變動一下,當這個參數變化的時候,強緩存都會失效並從新加載。這樣一來,靜態資源,部署之後就須要從新加載。這樣就比較完美的解決了問題。

進一步思考

這樣作是否是最完美的呢?很遺憾,不是。

至於有什麼更好的方法,歡迎你們留言討論。

相關文章
相關標籤/搜索