瀏覽器緩存知識點總結

瀏覽器緩存,也就是客戶端緩存,既是網頁性能優化裏面靜態資源相關優化的一大利器,也是無數web開發人員在工做過程不可避免的一大問題,因此在產品開發的時候咱們老是想辦法避免緩存產生,而在產品發佈之時又在想策略管理緩存提高網頁的訪問速度。瞭解瀏覽器的緩存命中原理,是開發web應用的基礎,本文着眼於此,學習瀏覽器緩存的相關知識,總結緩存避免和緩存管理的方法,結合具體的場景說明緩存的相關問題。但願能對有須要的人有所幫助。css

 

1. 瀏覽器緩存基本認識

它分爲強緩存和協商緩存: html

1)瀏覽器在加載資源時,先根據這個資源的一些http header判斷它是否命中強緩存,強緩存若是命中,瀏覽器直接從本身的緩存中讀取資源,不會發請求到服務器。好比某個css文件,若是瀏覽器在加載它所在的網頁時,這個css文件的緩存配置命中了強緩存,瀏覽器就直接從緩存中加載這個css,連請求都不會發送到網頁所在服務器;前端

2)當強緩存沒有命中的時候,瀏覽器必定會發送一個請求到服務器,經過服務器端依據資源的另一些http header驗證這個資源是否命中協商緩存,若是協商緩存命中,服務器會將這個請求返回,可是不會返回這個資源的數據,而是告訴客戶端能夠直接從緩存中加載這個資源,因而瀏覽器就又會從本身的緩存中去加載這個資源;java

3)強緩存與協商緩存的共同點是:若是命中,都是從客戶端緩存中加載資源,而不是從服務器加載資源數據;區別是:強緩存不發請求到服務器,協商緩存會發請求到服務器。webpack

4)當協商緩存也沒有命中的時候,瀏覽器直接從服務器加載資源數據。nginx

 

2. 強緩存的原理

當瀏覽器對某個資源的請求命中了強緩存時,返回的http狀態爲200,在chrome的開發者工具的network裏面size會顯示爲from cache,好比京東的首頁裏就有不少靜態資源配置了強緩存,用chrome打開幾回,再用f12查看network,能夠看到有很多請求就是從緩存中加載的:git

 

強緩存是利用Expires或者Cache-Control這兩個http response header實現的,它們都用來表示資源在客戶端緩存的有效期。github

Expires是http1.0提出的一個表示資源過時時間的header,它描述的是一個絕對時間,由服務器返回,用GMT格式的字符串表示,如:Expires:Thu, 31 Dec 2037 23:55:55 GMT,它的緩存原理是:web

 

1)瀏覽器第一次跟服務器請求一個資源,服務器在返回這個資源的同時,在respone的header加上Expires的header,如:ajax

 

2)瀏覽器在接收到這個資源後,會把這個資源連同全部response header一塊兒緩存下來(因此緩存命中的請求返回的header並非來自服務器,而是來自以前緩存的header);

3)瀏覽器再請求這個資源時,先從緩存中尋找,找到這個資源後,拿出它的Expires跟當前的請求時間比較,若是請求時間在Expires指定的時間以前,就能命中緩存,不然就不行。

4)若是緩存沒有命中,瀏覽器直接從服務器加載資源時,Expires Header在從新加載的時候會被更新。

Expires是較老的強緩存管理header,因爲它是服務器返回的一個絕對時間,在服務器時間與客戶端時間相差較大時,緩存管理容易出現問題,好比隨意修改下客戶端時間,就能影響緩存命中的結果。因此在http1.1的時候,提出了一個新的header,就是Cache-Control,這是一個相對時間,在配置緩存的時候,以秒爲單位,用數值表示,如:Cache-Control:max-age=315360000,它的緩存原理是:

1)瀏覽器第一次跟服務器請求一個資源,服務器在返回這個資源的同時,在respone的header加上Cache-Control的header,如:

 

2)瀏覽器在接收到這個資源後,會把這個資源連同全部response header一塊兒緩存下來;

3)瀏覽器再請求這個資源時,先從緩存中尋找,找到這個資源後,根據它第一次的請求時間和Cache-Control設定的有效期,計算出一個資源過時時間,再拿這個過時時間跟當前的請求時間比較,若是請求時間在過時時間以前,就能命中緩存,不然就不行。

4)若是緩存沒有命中,瀏覽器直接從服務器加載資源時,Cache-Control Header在從新加載的時候會被更新。

Cache-Control描述的是一個相對時間,在進行緩存命中的時候,都是利用客戶端時間進行判斷,因此相比較Expires,Cache-Control的緩存管理更有效,安全一些。

這兩個header能夠只啓用一個,也能夠同時啓用,當response header中,Expires和Cache-Control同時存在時,Cache-Control優先級高於Expires:

3. 強緩存的管理

前面介紹的是強緩存的原理,在實際應用中咱們會碰到須要強緩存的場景和不須要強緩存的場景,一般有2種方式來設置是否啓用強緩存:

1)經過代碼的方式,在web服務器返回的響應中添加Expires和Cache-Control Header;

2)經過配置web服務器的方式,讓web服務器在響應資源的時候統一添加Expires和Cache-Control Header。

好比在javaweb裏面,咱們可使用相似下面的代碼設置強緩存:

+ View code

還能夠經過相似下面的java代碼設置不啓用強緩存:

+ View code

tomcat還提供了一個ExpiresFilter專門用來配置強緩存,具體使用的方式可參考tomcat的官方文檔:http://tomcat.apache.org/tomcat-7.0-doc/config/filter.html#Expires_Filter

nginx和apache做爲專業的web服務器,都有專門的配置文件,能夠配置expires和cache-control,這方面的知識,若是你對運維感興趣的話,能夠在百度上搜索「nginx 設置 expires cache-control」或「apache 設置 expires cache-control」都能找到很多相關的文章。

因爲在開發的時候不會專門去配置強緩存,而瀏覽器又默認會緩存圖片,css和js等靜態資源,因此開發環境下常常會由於強緩存致使資源沒有及時更新而看不到最新的效果,解決這個問題的方法有不少,經常使用的有如下幾種:

1)直接ctrl+f5,這個辦法能解決頁面直接引用的資源更新的問題;

2)使用瀏覽器的隱私模式開發;

3)若是用的是chrome,能夠f12在network那裏把緩存給禁掉(這是個很是有效的方法):

 

4)在開發階段,給資源加上一個動態的參數,如css/index.css?v=0.0001,因爲每次資源的修改都要更新引用的位置,同時修改參數的值,因此操做起來不是很方便,除非你是在動態頁面好比jsp裏開發就能夠用服務器變量來解決(v=${sysRnd}),或者你能用一些前端的構建工具來處理這個參數修改的問題;

5)若是資源引用的頁面,被嵌入到了一個iframe裏面,能夠在iframe的區域右鍵單擊從新加載該頁面,以chrome爲例:

 

6)若是緩存問題出如今ajax請求中,最有效的解決辦法就是ajax的請求地址追加隨機數;

7)還有一種狀況就是動態設置iframe的src時,有可能也會由於緩存問題,致使看不到最新的效果,這時候在要設置的src後面添加隨機數也能解決問題;

8)若是你用的是grunt和gulp這種前端工具開發,經過它們的插件好比grunt-contrib-connect來啓動一個靜態服務器,則徹底不用擔憂開發階段的資源更新問題,由於在這個靜態服務器下的全部資源返回的respone header中,cache-control始終被設置爲不緩存:

4. 強緩存的應用

強緩存是前端性能優化最有力的工具,沒有之一,對於有大量靜態資源的網頁,必定要利用強緩存,提升響應速度。一般的作法是,爲這些靜態資源所有配置一個超時時間超長的Expires或Cache-Control,這樣用戶在訪問網頁時,只會在第一次加載時從服務器請求靜態資源,其它時候只要緩存沒有失效而且用戶沒有強制刷新的條件下都會從本身的緩存中加載,好比前面提到過的京東首頁緩存的資源,它的緩存過時時間都設置到了2026年:

 

然而這種緩存配置方式會帶來一個新的問題,就是發佈時資源更新的問題,好比某一張圖片,在用戶訪問第一個版本的時候已經緩存到了用戶的電腦上,當網站發佈新版本,替換了這個圖片時,已經訪問過第一個版本的用戶因爲緩存的設置,致使在默認的狀況下不會請求服務器最新的圖片資源,除非他清掉或禁用緩存或者強制刷新,不然就看不到最新的圖片效果。

這個問題已經有成熟的解決方案,具體內容可閱讀知乎這篇文章詳細瞭解:

http://www.zhihu.com/question/20790576

文章提到的東西都屬於理論上的解決方案,不過如今已經有不少前端工具可以實際地解決這個問題,因爲每一個工具涉及到的內容細節都有不少,本文沒有辦法一一深刻介紹。有興趣的能夠去了解下grunt gulp webpack fis 還有edp這幾個工具,基於這幾個工具都能解決這個問題,尤爲是fis和edp是百度推出的前端開發平臺,有現成的文檔能夠參考:

http://fis.baidu.com/fis3/api/index.html

http://ecomfe.github.io/edp/doc/initialization/install/

強緩存還有一點須要注意的是,一般都是針對靜態資源使用,動態資源須要慎用,除了服務端頁面能夠看做動態資源外,那些引用靜態資源的html也能夠看做是動態資源,若是這種html也被緩存,當這些html更新以後,可能就沒有機制可以通知瀏覽器這些html有更新,尤爲是先後端分離的應用裏,頁面都是純html頁面,每一個訪問地址可能都是直接訪問html頁面,這些頁面一般不增強緩存,以保證瀏覽器訪問這些頁面時始終請求服務器最新的資源。

 

5. 協商緩存的原理

當瀏覽器對某個資源的請求沒有命中強緩存,就會發一個請求到服務器,驗證協商緩存是否命中,若是協商緩存命中,請求響應返回的http狀態爲304而且會顯示一個Not Modified的字符串,好比你打開京東的首頁,按f12打開開發者工具,再按f5刷新頁面,查看network,能夠看到有很多請求就是命中了協商緩存的:

 

查看單個請求的Response Header,也能看到304的狀態碼和Not Modified的字符串,只要看到這個就可說明這個資源是命中了協商緩存,而後從客戶端緩存中加載的,而不是服務器最新的資源:

 

協商緩存是利用的是【Last-Modified,If-Modified-Since】和【ETag、If-None-Match】這兩對Header來管理的。

【Last-Modified,If-Modified-Since】的控制緩存的原理是:

1)瀏覽器第一次跟服務器請求一個資源,服務器在返回這個資源的同時,在respone的header加上Last-Modified的header,這個header表示這個資源在服務器上的最後修改時間:

 

2)瀏覽器再次跟服務器請求這個資源時,在request的header上加上If-Modified-Since的header,這個header的值就是上一次請求時返回的Last-Modified的值:

 

3)服務器再次收到資源請求時,根據瀏覽器傳過來If-Modified-Since和資源在服務器上的最後修改時間判斷資源是否有變化,若是沒有變化則返回304 Not Modified,可是不會返回資源內容;若是有變化,就正常返回資源內容。當服務器返回304 Not Modified的響應時,response header中不會再添加Last-Modified的header,由於既然資源沒有變化,那麼Last-Modified也就不會改變,這是服務器返回304時的response header:

4)瀏覽器收到304的響應後,就會從緩存中加載資源。

5)若是協商緩存沒有命中,瀏覽器直接從服務器加載資源時,Last-Modified Header在從新加載的時候會被更新,下次請求時,If-Modified-Since會啓用上次返回的Last-Modified值。

【Last-Modified,If-Modified-Since】都是根據服務器時間返回的header,通常來講,在沒有調整服務器時間和篡改客戶端緩存的狀況下,這兩個header配合起來管理協商緩存是很是可靠的,可是有時候也會服務器上資源其實有變化,可是最後修改時間卻沒有變化的狀況,而這種問題又很不容易被定位出來,而當這種狀況出現的時候,就會影響協商緩存的可靠性。因此就有了另一對header來管理協商緩存,這對header就是【ETag、If-None-Match】。它們的緩存管理的方式是:

1)瀏覽器第一次跟服務器請求一個資源,服務器在返回這個資源的同時,在respone的header加上ETag的header,這個header是服務器根據當前請求的資源生成的一個惟一標識,這個惟一標識是一個字符串,只要資源有變化這個串就不一樣,跟最後修改時間沒有關係,因此能很好的補充Last-Modified的問題:

 

2)瀏覽器再次跟服務器請求這個資源時,在request的header上加上If-None-Match的header,這個header的值就是上一次請求時返回的ETag的值:

 

3)服務器再次收到資源請求時,根據瀏覽器傳過來If-None-Match和而後再根據資源生成一個新的ETag,若是這兩個值相同就說明資源沒有變化,不然就是有變化;若是沒有變化則返回304 Not Modified,可是不會返回資源內容;若是有變化,就正常返回資源內容。與Last-Modified不同的是,當服務器返回304 Not Modified的響應時,因爲ETag從新生成過,response header中還會把這個ETag返回,即便這個ETag跟以前的沒有變化:

 

4)瀏覽器收到304的響應後,就會從緩存中加載資源。

 

6. 協商緩存的管理

協商緩存跟強緩存不同,強緩存不發請求到服務器,因此有時候資源更新了瀏覽器還不知道,可是協商緩存會發請求到服務器,因此資源是否更新,服務器確定知道。大部分web服務器都默認開啓協商緩存,並且是同時啓用【Last-Modified,If-Modified-Since】和【ETag、If-None-Match】,好比apache:

 

若是沒有協商緩存,每一個到服務器的請求,就都得返回資源內容,這樣服務器的性能會極差。

【Last-Modified,If-Modified-Since】和【ETag、If-None-Match】通常都是同時啓用,這是爲了處理Last-Modified不可靠的狀況。有一種場景須要注意:

分佈式系統裏多臺機器間文件的Last-Modified必須保持一致,以避免負載均衡到不一樣機器致使比對失敗;

分佈式系統儘可能關閉掉ETag(每臺機器生成的ETag都會不同);

京東頁面的資源請求,返回的repsones header就只有Last-Modified,沒有ETag:

 

協商緩存須要配合強緩存使用,你看前面這個截圖中,除了Last-Modified這個header,還有強緩存的相關header,由於若是不啓用強緩存的話,協商緩存根本沒有意義。

 

7. 瀏覽器行爲對緩存的影響

若是資源已經被瀏覽器緩存下來,在緩存失效以前,再次請求時,默認會先檢查是否命中強緩存,若是強緩存命中則直接讀取緩存,若是強緩存沒有命中則發請求到服務器檢查是否命中協商緩存,若是協商緩存命中,則告訴瀏覽器仍是能夠從緩存讀取,不然才從服務器返回最新的資源。這是默認的處理方式,這個方式可能被瀏覽器的行爲改變:

1)當ctrl+f5強制刷新網頁時,直接從服務器加載,跳過強緩存和協商緩存;

2)當f5刷新網頁時,跳過強緩存,可是會檢查協商緩存;

原文連接:https://mp.weixin.qq.com/s/Ucxocwgx5N9KlSeiUxtOng

相關文章
相關標籤/搜索