每個瀏覽器都自帶了 HTTP 緩存實現(xiàn)功能。您只需要確保每個服務器響應都提供正確的 HTTP 標頭指令厕隧,以指示瀏覽器何時可以緩存響應以及可以緩存多久企量。緩存并重復利用之前獲取的資源是優(yōu)化性能的一個重要方面涵防。
在 http1.0 時代,給客戶端設定緩存方式可通過兩個字段——Pragma和Expires來規(guī)范闹伪。
一,Pragma
有 Pragma:no-cache;表示禁用緩存壮池;
二偏瓤,Expires
exprires是啟用緩存 的方式,與Pragma剛好相對應
正確的格式是 expries:GMT時間格式椰憋;( Expires的值對應一個GMT(格林尼治時間)厅克,比如Mon, 22 Jul 2017 11:12:01 GMT來告訴瀏覽器資源緩存過期時間,如果還沒過該時間點則不發(fā)請求橙依。)
上述兩種方式如果同時存在证舟,Pragma的優(yōu)先級大于Expires。
針對上述的“Expires時間是相對服務器而言票编,無法保證和客戶端時間統(tǒng)一”的問題,http1.1新增了 Cache-Control 來定義緩存過期時間卵渴。
三慧域,Cache-Control
- 每個資源都可通過 Cache-Control HTTP 標頭定義其緩存策略
- Cache-Control 指令控制誰在什么條件下可以緩存響應以及可以緩存多久。
從性能優(yōu)化的角度來說浪读,最佳請求是無需與服務器通信的請求:您可以通過響應的本地副本消除所有網絡延遲昔榴,以及避免數(shù)據傳送的流量費用辛藻。為實現(xiàn)此目的,HTTP 規(guī)范允許服務器返回 Cache-Control 指令互订,這些指令控制瀏覽器和其他中間緩存如何緩存各個響應以及緩存多久吱肌。
注:Cache-Control 標頭是在 HTTP/1.1 規(guī)范中定義的,取代了之前用來定義響應緩存策略的標頭(例如 Expires)仰禽。所有現(xiàn)代瀏覽器都支持 Cache-Control氮墨,因此,使用它就夠了吐葵。
注意:若報文中同時出現(xiàn)了 Expires 和 Cache-Control规揪,則以 Cache-Control 為準。
也就是說優(yōu)先級從高到低分別是 Pragma -> Cache-Control -> Expires 温峭。
Cache-Control也是一個通用首部字段猛铅,這意味著它能分別在請求報文和響應報文中使用。在RFC中規(guī)范了 Cache-Control 的格式為:
"Cache-Control" ":" cache-directive
作為請求首部時凤藏,cache-directive 的可選值有:
作為響應首部時奸忽,cache-directive 的可選值有:
Cache-Control 允許自由組合可選值,例如:
Cache-Control: max-age=3600, must-revalidate
定義最佳 Cache-Control 策略
按照以上決策樹為您的應用使用的特定資源或一組資源確定最佳緩存策略揖庄。在理想的情況下栗菜,您的目標應該是在客戶端上緩存盡可能多的響應,緩存盡可能長的時間抠艾,并且為每個響應提供驗證令牌苛萎,以實現(xiàn)高效的重新驗證。
如果客戶端向服務器發(fā)了請求检号,那么是否意味著一定要讀取回該資源的整個實體內容呢腌歉?
我們試著這么想——客戶端上某個資源保存的緩存時間過期了,但這時候其實服務器并沒有更新過這個資源齐苛,如果這個資源數(shù)據量很大翘盖,客戶端要求服務器再把這個東西重新發(fā)一遍過來,是否非常浪費帶寬和時間呢凹蜂?
答案是肯定的馍驯,那么是否有辦法讓服務器知道客戶端現(xiàn)在存有的緩存文件,其實跟自己所有的文件是一致的玛痊,然后直接告訴客戶端說“這東西你直接用緩存里的就可以了汰瘫,我這邊沒更新過呢,就不再傳一次過去了”擂煞。
為了讓客戶端與服務器之間能實現(xiàn)緩存文件是否更新的驗證混弥、提升緩存的復用率,Http1.1新增了幾個首部字段來做這件事情对省。
1.Last-Modified
服務器將資源傳遞給客戶端時蝗拿,會將資源最后更改的時間以“Last-Modified: GMT”的形式加在實體首部上一起返回給客戶端
客戶端會為資源標記上該信息晾捏,下次再次請求時,會把該信息附帶在請求報文中一并帶給服務器去做檢查哀托,若傳遞的時間值與服務器上該資源最終修改時間是一致的惦辛,則說明該資源沒有被修改過,直接返回304狀態(tài)碼仓手,內容為空胖齐,這樣就節(jié)省了傳輸數(shù)據量 。如果兩個時間不一致俗或,則服務器會發(fā)回該資源并返回200狀態(tài)碼市怎,和第一次請求時類似。
這樣保證不向客戶端重復發(fā)出資源辛慰,也保證當服務器有變化時区匠,客戶端能夠得到最新的資源。一個304響應比一個靜態(tài)資源通常小得多帅腌,這樣就節(jié)省了網絡帶寬驰弄。
Last-Modified 存在一定問題,如果在服務器上速客,一個資源被修改了戚篙,但其實際內容根本沒發(fā)生改變,會因為Last-Modified時間匹配不上而返回了整個實體給客戶端(即使客戶端緩存里有個一模一樣的資源)溺职。
2.ETag
為了解決上述Last-Modified可能存在的不準確的問題岔擂,Http1.1還推出了 ETag 實體首部字段。 服務器會通過某種算法浪耘,給資源計算得出一個唯一標志符(比如md5標志)乱灵,在把資源響應給客戶端的時候,會在實體首部加上“ETag: 唯一標識符”一起返回給客戶端七冲。例如:
Etag: "5d8c72a5edda8d6a:3239"
客戶端會保留該 ETag 字段痛倚,并在下一次請求時將其一并帶過去給服務器。服務器只需要比較客戶端傳來的ETag跟自己服務器上該資源的ETag是否一致澜躺,就能很好地判斷資源相對客戶端而言是否被修改過了蝉稳。
如果服務器發(fā)現(xiàn)ETag匹配不上,那么直接以常規(guī)GET 200回包形式將新的資源(當然也包括了新的ETag)發(fā)給客戶端掘鄙;如果ETag是一致的耘戚,則直接返回304知會客戶端直接使用本地緩存即可。
- 服務器使用 ETag HTTP 標頭傳遞驗證令牌操漠。
- 驗證令牌可實現(xiàn)高效的資源更新檢查:資源未發(fā)生變化時不會傳送任何數(shù)據收津。
這正是驗證令牌(在 ETag 標頭中指定)旨在解決的問題。服務器生成并返回的隨機令牌通常是文件內容的哈希值或某個其他指紋∨蠼兀客戶端不需要了解指紋是如何生成的,只需在下一次請求時將其發(fā)送至服務器吧黄。如果指紋仍然相同部服,則表示資源未發(fā)生變化,您就可以跳過下載拗慨。
在上例中廓八,客戶端自動在“If-None-Match” HTTP 請求標頭內提供 ETag 令牌。服務器根據當前資源核對令牌赵抢。如果它未發(fā)生變化剧蹂,服務器將返回“304 Not Modified”響應,告知瀏覽器緩存中的響應未發(fā)生變化烦却,可以再延用 120 秒宠叼。請注意,您不必再次下載響應其爵,這節(jié)約了時間和帶寬冒冬。
作為網絡開發(fā)者,您如何利用高效的重新驗證摩渺?瀏覽器會替我們完成所有工作:它會自動檢測之前是否指定了驗證令牌简烤,它會將驗證令牌追加到發(fā)出的請求上,并且它會根據從服務器接收的響應在必要時更新緩存時間戳摇幻。我們唯一要做的就是確保服務器提供必要的 ETag 令牌横侦。檢查您的服務器文檔中有無必要的配置標志。
不過绰姻,如果您想更新或廢棄緩存的響應枉侧,該怎么辦?
注意:假定您已告訴訪問者將某個 CSS 樣式表緩存長達 24 小時 (max-age=86400)龙宏,但設計人員剛剛提交了一個您希望所有用戶都能使用的更新棵逊。您該如何通知擁有現(xiàn)在“已過時”的 CSS 緩存副本的所有訪問者更新其緩存?在不更改資源網址的情況下银酗,您做不到辆影。
瀏覽器緩存響應后,緩存的版本將一直使用到過期(由 max-age 或 expires 決定)黍特,或一直使用到由于某種其他原因從緩存中刪除蛙讥,例如用戶清除了瀏覽器緩存。因此灭衷,構建網頁時次慢,不同的用戶可能最終使用的是文件的不同版本;剛獲取了資源的用戶將使用新版本的響應,而緩存了早期(但仍有效)副本的用戶將使用舊版本的響應迫像。
所以劈愚,如何才能魚和熊掌兼得:客戶端緩存和快速更新?
您可以在資源內容發(fā)生變化時更改它的網址闻妓,強制用戶下載新響應菌羽。通常情況下,可以通過在文件名中嵌入文件的指紋或版本號來實現(xiàn) - 例如 style.x234dff.css由缆。
因為能夠定義每個資源的緩存策略注祖,所以您可以定義“緩存層次結構”,這樣不但可以控制每個響應的緩存時間均唉,還可以控制訪問者看到新版本的速度是晨。為了進行說明,我們一起分析一下上面的示例:
- HTML 被標記為“no-cache”舔箭,這意味著瀏覽器在每次請求時都始終會重新驗證文檔罩缴,并在內容變化時獲取最新版本。此外层扶,在 HTML 標記內靴庆,您在 CSS 和 JavaScript 資產的網址中嵌入指紋:如果這些文件的內容發(fā)生變化,網頁的 HTML 也會隨之改變怒医,并會下載 HTML 響應的新副本炉抒。
- 允許瀏覽器和中間緩存(例如 CDN)緩存 CSS,并將 CSS 設置為 1 年后到期稚叹。請注意焰薄,您可以放心地使用 1 年的“遠期過期”,因為您在文件名中嵌入了文件的指紋:CSS 更新時網址也會隨之變化扒袖。
- JavaScript 同樣設置為 1 年后到期塞茅,但標記為 private,這或許是因為它包含的某些用戶私人數(shù)據是 CDN 不應緩存的季率。
- 圖像緩存時不包含版本或唯一指紋野瘦,并設置為 1 天后到期。
您可以組合使用 ETag飒泻、Cache-Control 和唯一網址來實現(xiàn)一舉多得:較長的過期時間鞭光、控制可以緩存響應的位置以及隨需更新。
不存在什么最佳緩存策略泞遗。
您需要根據通信模式惰许、提供的數(shù)據類型以及應用特定的數(shù)據更新要求,為每個資源定義和配置合適的設置史辙,以及整體的“緩存層次結構”汹买。
在制定緩存策略時佩伤,您需要牢記下面這些技巧和方法:
- 使用一致的網址:如果您在不同的網址上提供相同的內容,將會多次獲取和存儲這些內容晦毙。提示:請注意生巡,網址區(qū)分大小寫。
- 確保服務器提供驗證令牌 (ETag):有了驗證令牌见妒,當服務器上的資源未發(fā)生變化時障斋,就不需要傳送相同的字節(jié)。
- 為每個資源確定最佳緩存周期:不同的資源可能有不同的更新要求徐鹤。為每個資源審核并確定合適的 max-age。
- 確定最適合您的網站的緩存層次結構:您可以通過為 HTML 文檔組合使用包含內容指紋的資源網址和短時間或 no-cache 周期邀层,來控制客戶端獲取更新的速度返敬。
- 最大限度減少攪動:某些資源的更新比其他資源頻繁。如果資源的特定部分(例如 JavaScript 函數(shù)或 CSS 樣式集)會經常更新寥院,可以考慮將其代碼作為單獨的文件提供劲赠。這樣一來,每次獲取更新時秸谢,其余內容(例如變化不是很頻繁的內容庫代碼)可以從緩存獲取凛澎,從而最大限度減少下載的內容大小。
參考資料
HTTP 緩存
HTTP緩存控制小結