以前一直對瀏覽器緩存只能描述一個大概,深層次的原理不能描述上來;終於在前端的兩次面試過程當中被問倒下,爲了泄恨,查閱一些資料最終對其有了一個更深刻的理解,廢話很少說,趕忙來看看瀏覽器緩存的那些事吧,有不對的地方,請各位不吝賜教啊。前端
本文主要講解瀏覽器端的緩存,緩存的做用是不言而喻的,可以極大的改善網頁性能,提升用戶體驗。面試
一、瀏覽器緩存
緩存這東西,第一次必須獲取到資源後,而後根據返回的信息來告訴如何緩存資源,可能採用的是強緩存,也可能告訴客戶端瀏覽器是協商緩存,這都須要根據響應的header內容來決定的。下面用兩幅圖來描述瀏覽器的緩存是怎麼玩的,讓你們有個大概的認知。瀏覽器
瀏覽器第一次請求時:緩存
瀏覽器後續在進行請求時:服務器
從上圖能夠知道,瀏覽器緩存包含兩種類型,即強緩存(也叫本地緩存)和協商緩存,瀏覽器在第一次請求發生後,再次請求時:性能
- 瀏覽器在請求某一資源時,會先獲取該資源緩存的header信息,判斷是否命中強緩存(cache-control和expires信息),若命中直接從緩存中獲取資源信息,包括緩存header信息;本次請求根本就不會與服務器進行通訊;在firebug下能夠查看某個具備強緩存資源返回的信息,例如本地firebug查看的一個強緩存js文件
- 若是沒有命中強緩存,瀏覽器會發送請求到服務器,請求會攜帶第一次請求返回的有關緩存的header字段信息(Last-Modified/If-Modified-Since和Etag/If-None-Match),由服務器根據請求中的相關header信息來比對結果是否協商緩存命中;若命中,則服務器返回新的響應header信息更新緩存中的對應header信息,可是並不返回資源內容,它會告知瀏覽器能夠直接從緩存獲取;不然返回最新的資源內容
強緩存與協商緩存的區別,能夠用下表來進行描述:spa
|
獲取資源形式 |
狀態碼 |
發送請求到服務器 |
強緩存 |
從緩存取 |
200(from cache) |
否,直接從緩存取 |
協商緩存 |
從緩存取 |
304(not modified) |
是,正如其名,經過服務器來告知緩存是否可用 |
二、強緩存相關的header字段
強緩存上面已經介紹了,直接從緩存中獲取資源而不通過服務器;與強緩存相關的header字段有兩個:代理
- expires,這是http1.0時的規範;它的值爲一個絕對時間的GMT格式的時間字符串,如Mon, 10 Jun 2015 21:31:12 GMT,若是發送請求的時間在expires以前,那麼本地緩存始終有效,不然就會發送請求到服務器來獲取資源
- cache-control:max-age=number,這是http1.1時出現的header信息,主要是利用該字段的max-age值來進行判斷,它是一個相對值;資源第一次的請求時間和Cache-Control設定的有效期,計算出一個資源過時時間,再拿這個過時時間跟當前的請求時間比較,若是請求時間在過時時間以前,就能命中緩存,不然就不行;cache-control除了該字段外,還有下面幾個比較經常使用的設置值:
- no-cache:不使用本地緩存。須要使用緩存協商,先與服務器確認返回的響應是否被更改,若是以前的響應中存在ETag,那麼請求的時候會與服務端驗證,若是資源未被更改,則能夠避免從新下載。
- no-store:直接禁止遊覽器緩存數據,每次用戶請求該資源,都會向服務器發送一個請求,每次都會下載完整的資源。
- public:能夠被全部的用戶緩存,包括終端用戶和CDN等中間代理服務器。
- private:只能被終端用戶的瀏覽器緩存,不容許CDN等中繼緩存服務器對其緩存。
注意:若是cache-control與expires同時存在的話,cache-control的優先級高於expiresblog
三、協商緩存相關的header字段
協商緩存都是由服務器來肯定緩存資源是否可用的,因此客戶端與服務器端要經過某種標識來進行通訊,從而讓服務器判斷請求資源是否能夠緩存訪問,這主要涉及到下面兩組header字段,這兩組搭檔都是成對出現的,即第一次請求的響應頭帶上某個字段(Last-Modified或者Etag),則後續請求則會帶上對應的請求字段(If-Modified-Since或者If-None-Match),若響應頭沒有Last-Modified或者Etag字段,則請求頭也不會有對應的字段。資源
- Last-Modified/If-Modified-Since
兩者的值都是GMT格式的時間字符串,具體過程:
- 瀏覽器第一次跟服務器請求一個資源,服務器在返回這個資源的同時,在respone的header加上Last-Modified的header,這個header表示這個資源在服務器上的最後修改時間
- 瀏覽器再次跟服務器請求這個資源時,在request的header上加上If-Modified-Since的header,這個header的值就是上一次請求時返回的Last-Modified的值
- 服務器再次收到資源請求時,根據瀏覽器傳過來If-Modified-Since和資源在服務器上的最後修改時間判斷資源是否有變化,若是沒有變化則返回304 Not Modified,可是不會返回資源內容;若是有變化,就正常返回資源內容。當服務器返回304 Not Modified的響應時,response header中不會再添加Last-Modified的header,由於既然資源沒有變化,那麼Last-Modified也就不會改變,這是服務器返回304時的response header
- 瀏覽器收到304的響應後,就會從緩存中加載資源
- 若是協商緩存沒有命中,瀏覽器直接從服務器加載資源時,Last-Modified的Header在從新加載的時候會被更新,下次請求時,If-Modified-Since會啓用上次返回的Last-Modified值
- Etag/If-None-Match
這兩個值是由服務器生成的每一個資源的惟一標識字符串,只要資源有變化就這個值就會改變;其判斷過程與Last-Modified/If-Modified-Since相似,與Last-Modified不同的是,當服務器返回304 Not Modified的響應時,因爲ETag從新生成過,response header中還會把這個ETag返回,即便這個ETag跟以前的沒有變化。
四、既生Last-Modified何生Etag
你可能會以爲使用Last-Modified已經足以讓瀏覽器知道本地的緩存副本是否足夠新,爲何還須要Etag呢?HTTP1.1中Etag的出現主要是爲了解決幾個Last-Modified比較難解決的問題:
這時,利用Etag可以更加準確的控制緩存,由於Etag是服務器自動生成或者由開發者生成的對應資源在服務器端的惟一標識符。
Last-Modified與ETag是能夠一塊兒使用的,服務器會優先驗證ETag,一致的狀況下,纔會繼續比對Last-Modified,最後才決定是否返回304。
五、用戶的行爲對緩存的影響
盜用網上的一張圖,基本能描述用戶行爲對緩存的影響
六、強緩存如何從新加載緩存緩存過的資源
上面說到,使用強緩存時,瀏覽器不會發送請求到服務端,根據設置的緩存時間瀏覽器一直從緩存中獲取資源,在這期間若資源產生了變化,瀏覽器就在緩存期內就一直得不到最新的資源,那麼如何防止這種事情發生呢?
經過更新頁面中引用的資源路徑,讓瀏覽器主動放棄緩存,加載新資源。
相似下圖所示:
這樣每次文件改變後就會生成新的query值,這樣query值不一樣,也就是頁面引用的資源路徑不一樣了,以前緩存過的資源就被瀏覽器忽略了,由於資源請求的路徑變了。
具體的強烈推薦看知乎的這篇問答中張雲龍的回答 https://www.zhihu.com/question/20790576