瀏覽器緩存知識小結及應用

来源:http://www.cnblogs.com/lyzg/archive/2016/01/15/5125934.html
-Advertisement-
Play Games

瀏覽器緩存,也就是客戶端緩存,既是網頁性能優化裡面靜態資源相關優化的一大利器,也是無數web開發人員在工作過程不可避免的一大問題,所以在產品開發的時候我們總是想辦法避免緩存產生,而在產品發佈之時又在想策略管理緩存提升網頁的訪問速度。瞭解瀏覽器的緩存命中原理,是開發web應用的基礎,本文著眼於此,學習...


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

1. 瀏覽器緩存基本認識

它分為強緩存和協商緩存:
1)瀏覽器在載入資源時,先根據這個資源的一些http header判斷它是否命中強緩存,強緩存如果命中,瀏覽器直接從自己的緩存中讀取資源,不會發請求到伺服器。比如某個css文件,如果瀏覽器在載入它所在的網頁時,這個css文件的緩存配置命中了強緩存,瀏覽器就直接從緩存中載入這個css,連請求都不會發送到網頁所在伺服器;

2)當強緩存沒有命中的時候,瀏覽器一定會發送一個請求到伺服器,通過伺服器端依據資源的另外一些http header驗證這個資源是否命中協商緩存,如果協商緩存命中,伺服器會將這個請求返回,但是不會返回這個資源的數據,而是告訴客戶端可以直接從緩存中載入這個資源,於是瀏覽器就又會從自己的緩存中去載入這個資源;

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

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

2. 強緩存的原理

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

image

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

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

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

image

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,如:

image

2)瀏覽器在接收到這個資源後,會把這個資源連同所有response header一起緩存下來;

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

4)如果緩存沒有命中,瀏覽器直接從伺服器載入資源時,Cache-Control Header在重新載入的時候會被更新。

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

這兩個header可以只啟用一個,也可以同時啟用,當response header中,Expires和Cache-Control同時存在時,Cache-Control優先順序高於Expires:

image

3. 強緩存的管理

前面介紹的是強緩存的原理,在實際應用中我們會碰到需要強緩存的場景和不需要強緩存的場景,通常有2種方式來設置是否啟用強緩存:

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

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

比如在javaweb裡面,我們可以使用類似下麵的代碼設置強緩存:

java.util.Date date = new java.util.Date();    
response.setDateHeader("Expires",date.getTime()+20000); //Expires:過時期限值 
response.setHeader("Cache-Control", "public"); //Cache-Control來控制頁面的緩存與否,public:瀏覽器和緩存伺服器都可以緩存頁面信息;
response.setHeader("Pragma", "Pragma"); //Pragma:設置頁面是否緩存,為Pragma則緩存,no-cache則不緩存

還可以通過類似下麵的java代碼設置不啟用強緩存:

response.setHeader( "Pragma", "no-cache" );   
response.setDateHeader("Expires", 0);   
response.addHeader( "Cache-Control", "no-cache" );//瀏覽器和緩存伺服器都不應該緩存頁面信息

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那裡把緩存給禁掉(這是個非常有效的方法):

image

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

5)如果資源引用的頁面,被嵌入到了一個iframe裡面,可以在iframe的區域右鍵單擊重新載入該頁面,以chrome為例:

image

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

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

8)如果你用的是grunt和gulp這種前端工具開發,通過它們的插件比如grunt-contrib-connect來啟動一個靜態伺服器,則完全不用擔心開發階段的資源更新問題,因為在這個靜態伺服器下的所有資源返回的respone header中,cache-control始終被設置為不緩存:

image

4. 強緩存的應用

強緩存是前端性能優化最有力的工具,沒有之一,對於有大量靜態資源的網頁,一定要利用強緩存,提高響應速度。通常的做法是,為這些靜態資源全部配置一個超時時間超長的Expires或Cache-Control,這樣用戶在訪問網頁時,只會在第一次載入時從伺服器請求靜態資源,其它時候只要緩存沒有失效並且用戶沒有強制刷新的條件下都會從自己的緩存中載入,比如前面提到過的京東首頁緩存的資源,它的緩存過期時間都設置到了2026年:

image

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

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

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,可以看到有不少請求就是命中了協商緩存的:

image

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

image

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

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

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

image

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:

image

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的問題:

image

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

image

3)伺服器再次收到資源請求時,根據瀏覽器傳過來If-None-Match和然後再根據資源生成一個新的ETag,如果這兩個值相同就說明資源沒有變化,否則就是有變化;如果沒有變化則返回304 Not Modified,但是不會返回資源內容;如果有變化,就正常返回資源內容。與Last-Modified不一樣的是,當伺服器返回304 Not Modified的響應時,由於ETag重新生成過,response header中還會把這個ETag返回,即使這個ETag跟之前的沒有變化:

image

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

6. 協商緩存的管理

協商緩存跟強緩存不一樣,強緩存不發請求到伺服器,所以有時候資源更新了瀏覽器還不知道,但是協商緩存會發請求到伺服器,所以資源是否更新,伺服器肯定知道。大部分web伺服器都預設開啟協商緩存,而且是同時啟用【Last-Modified,If-Modified-Since】和【ETag、If-None-Match】,比如apache:

image

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

【Last-Modified,If-Modified-Since】和【ETag、If-None-Match】一般都是同時啟用,這是為了處理Last-Modified不可靠的情況。有一種場景需要註意:

分散式系統里多台機器間文件的Last-Modified必須保持一致,以免負載均衡到不同機器導致比對失敗;

分散式系統儘量關閉掉ETag(每台機器生成的ETag都會不一樣);

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

image

協商緩存需要配合強緩存使用,你看前面這個截圖中,除了Last-Modified這個header,還有強緩存的相關header,因為如果不啟用強緩存的話,協商緩存根本沒有意義。

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

如果資源已經被瀏覽器緩存下來,在緩存失效之前,再次請求時,預設會先檢查是否命中強緩存,如果強緩存命中則直接讀取緩存,如果強緩存沒有命中則發請求到伺服器檢查是否命中協商緩存,如果協商緩存命中,則告訴瀏覽器還是可以從緩存讀取,否則才從伺服器返回最新的資源。這是預設的處理方式,這個方式可能被瀏覽器的行為改變:

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

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

謝謝閱讀:)希望本文的內容能對你有所幫助~


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 選擇器(selector)是CSS中很重要的概念,所有HTML語言中的標記都是通過不同的CSS選擇器進行控制的。用戶只需要通過選擇器對不同的HTML標簽進行控制,並賦予各種樣式聲明,即可實現各種效果。 1.** { margin: 0; padding: 0; } 星號選擇器用於選取頁面中的所有元....
  • 瀏覽器檢測通常都是通過分析用戶代理字元串(navigator.userAgent)來進行檢測。由於國內瀏覽器用的內核多是國外的,所以很多特性無法與國外瀏覽器區分,所以要先檢測國外瀏覽器,再檢測國內瀏覽器。這樣當國內瀏覽器沒有找到匹配項時就是內核預設的國外瀏覽器。function checkChine...
  • 對於瀏覽器相容問題,我們應該碰到很多了,在平時寫一些頁面時,在IE8、IE9上可能好好的,當我們在IE6、IE7或者是其他的瀏覽器上再瀏覽這些頁面時,可能會發現我們的頁面已經面目全非了,作為一名前端開發人員,這是最讓人發瘋的問題了,那麼我們該怎麼解決這些問題呢?首先,我們寫的頁面在不同的瀏覽器上之所...
  • 網站快速載入,是提供良好用戶體驗的前提。然而,網站功能的不斷增多,程式包的不斷臃腫,導致網站訪問時較大的下載量,最終影響了響應速度。沒有一個用戶喜歡等待,如何減少代碼量,為網站減去過多負擔,Craig Buckler在sitepoint網站發表了一篇文章《10 Quick and Easy Fixe...
  • JS中的三種元素綁定的方法總結//"數據綁定":在給#ul1動態新增加數據,ary這個json數據中有幾(7)條我們就增加幾(7)條在我們的HTML結構中事先的先放三個li,並對這三個元素標簽設置一個效果changeBg();當這個方法執行的時候實現我們滑鼠滑倒li上時,他的背景顏色改變,划過後背景...
  • JS中null和undefined的區別: null屬於當前的值為空,以後會給其賦值的// undefined屬於非人為設置,意料之外的沒有// 例如:耿大大的女朋友是null、耿大大的男朋友是undefined//// 1、null// 1)在定義變數的時候手動的設置為nu...
  • 基於CSS3滑鼠經過立體翻轉切換特效。這是一款基於jQuery+CSS3實現的滑鼠經過封面和詳情3D翻轉切換特效。效果圖如下:線上預覽源碼下載實現的代碼。html代碼: 定期寶年利率:7%-...
  • Ext.form.TimeField:配置項:maxValue:列表中允許的最大時間maxText:當時間大於最大值時的錯誤提示信息minValue:列表中允許的最小時間minText:當時間小於最小值時的錯誤提示信息increment:兩個相鄰選項間的時間間隔,預設為15分鐘format:顯示格式...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...