時(shí)間:2016-12-12 17:51:30
作者: zhongxia
零、前言
這里主要寫的是理論,具體實(shí)踐的比較少,后期寫一個(gè)實(shí)踐教程励稳,內(nèi)容基本都是從參考文章里面抄過來的【看完文章,順便寫做下筆記局蚀,加深理解麦锯。】
瀏覽器緩存琅绅,也就是客戶端緩存,既是網(wǎng)頁性能優(yōu)化里面靜態(tài)資源相關(guān)優(yōu)化的一大利器鹅巍,也是無數(shù)web開發(fā)人員在工作過程不可避免的一大問題千扶,所以在產(chǎn)品開發(fā)的時(shí)候我們總是想辦法避免緩存產(chǎn)生,而在產(chǎn)品發(fā)布之時(shí)又在想策略管理緩存提升網(wǎng)頁的訪問速度骆捧。
了解瀏覽器的緩存命中原理澎羞,是開發(fā)web應(yīng)用的基礎(chǔ),本文著眼于此敛苇,學(xué)習(xí)瀏覽器緩存的相關(guān)知識(shí)妆绞,總結(jié)緩存避免和緩存管理的方法顺呕,結(jié)合具體的場(chǎng)景說明緩存的相關(guān)問題。希望能對(duì)有需要的人有所幫助括饶。
一株茶、瀏覽器緩存基本認(rèn)識(shí)
1. 強(qiáng)緩存 和 協(xié)商緩存
瀏覽器在加載資源的時(shí),先根據(jù) http header 判斷它是否命中強(qiáng)緩存.
命中強(qiáng)緩存:瀏覽器直接從自己緩存中讀取資源图焰,不發(fā)送請(qǐng)求到服務(wù)器
-
不命中強(qiáng)緩存:瀏覽器發(fā)送一個(gè)請(qǐng)求到服務(wù)器启盛,服務(wù)器根據(jù)資源的另外一些 http header 驗(yàn)證 該資源 是否命中 協(xié)商緩存
命中協(xié)商緩存:將請(qǐng)求返回,但不是返回該資源的數(shù)據(jù)技羔,而是告訴瀏覽器可以直接從緩存中加載這個(gè)資源僵闯。
不命中協(xié)商緩存:服務(wù)器返回該資源數(shù)據(jù)
2. 異同點(diǎn) 和 關(guān)系
共同點(diǎn):命中,都是從瀏覽器緩存中加載資源
不同點(diǎn):強(qiáng)緩存不發(fā)送請(qǐng)求到服務(wù)器藤滥,協(xié)商緩存會(huì)發(fā)送請(qǐng)求鳖粟。
必須開啟強(qiáng)緩存,協(xié)商緩存才會(huì)起作用
二拙绊、強(qiáng)緩存原理
什么是協(xié)商緩存牺弹?
如圖,返回http狀態(tài)為200时呀,size為 form cache 的就是強(qiáng)緩存
1. HTTP Response Header 看強(qiáng)緩存
Expires
HTTP1.0 時(shí)代张漂,Expires 【表示資源過期時(shí)間】【緩存過期的下一個(gè)時(shí)間 必須GMT 格式】
可能存在問題:服務(wù)器時(shí)間和客戶端時(shí)間不一致,因此 HTTP1.1 出了一些 Cache-control
如圖:
Cache-control
HTTP1.1 時(shí)代谨娜,Cache-control: 過期時(shí)間【緩存多少毫秒】
如圖:
例子:
Expires步驟
瀏覽器第一次請(qǐng)求資源航攒,服務(wù)器在返回的同時(shí),會(huì)在 response 的 header 上加上 Expires
瀏覽器收到資源后趴梢,把資源和 Expires 一起緩存下來
第二次請(qǐng)求資源時(shí)漠畜,拿出Expires和當(dāng)前請(qǐng)求時(shí)間比較下,如果還未過期坞靶,則直接從緩存中讀取出來憔狞,【這個(gè)就叫命中緩存】
沒有命中的話,去服務(wù)端請(qǐng)求彰阴,走協(xié)商緩存道路瘾敢,最后返回時(shí),會(huì)返回一個(gè)新的 Expires 尿这, 瀏覽器在緩存下來簇抵。
Cache-control 步驟
和 Expires一樣,只是 過期時(shí)間=當(dāng)前時(shí)間+Cache-control 的 Max-age 緩存毫秒數(shù)
Cache-control 和 Expires
- 這兩個(gè)可以一起用射众,也可以只用其中一個(gè) 【一起用碟摆,優(yōu)先級(jí): Cache-Control > Expires】
- Expires 是比較老的強(qiáng)緩存管理Header,因?yàn)樗墙^對(duì)時(shí)間叨橱,而服務(wù)器時(shí)間和客戶端時(shí)間相差大典蜕,或者修改客戶端時(shí)間断盛,就會(huì)影響緩存命中率
- Cache-control 是相對(duì)時(shí)間,受到的影響小一些
如何設(shè)置強(qiáng)緩存?
1. 代碼形式
通過代碼的形式愉舔,在web服務(wù)器的返回響應(yīng) header 中添加 Expies钢猛, Cache-control
java.util.Date date = new java.util.Date();
response.setDateHeader("Expires",date.getTime()+20000); //Expires:過時(shí)期限值
response.setHeader("Cache-Control", "public"); //Cache-Control來控制頁面的緩存與否,public:瀏覽器和緩存服務(wù)器都可以緩存頁面信息;
response.setHeader("Pragma", "Pragma"); //Pragma:設(shè)置頁面是否緩存屑宠,為Pragma則緩存厢洞,no-cache則不緩存
設(shè)置不緩存
response.setHeader( "Pragma", "no-cache" );
response.setDateHeader("Expires", 0);
response.addHeader( "Cache-Control", "no-cache" );//瀏覽器和緩存服務(wù)器都不應(yīng)該緩存頁面信息
2. web服務(wù)器配置
讓web服務(wù)器在響應(yīng)資源的時(shí)候統(tǒng)一添加Expires和Cache-Control Header
nginx和apache作為專業(yè)的web服務(wù)器,都有專門的配置文件典奉,可以配置expires和cache-control躺翻,這方面的知識(shí),如果你對(duì)運(yùn)維感興趣的話卫玖,可以在百度上搜索“nginx 設(shè)置 expires cache-control”或“apache 設(shè)置 expires cache-control”都能找到不少相關(guān)的文章公你。
3. 強(qiáng)緩存的應(yīng)用
強(qiáng)緩存是前端性能優(yōu)化最有力的工具,沒有之一假瞬。
大量的靜態(tài)資源網(wǎng)站陕靠,一定要利用強(qiáng)緩存,提高響應(yīng)速度
例子脱茉,京東頁面剪芥,強(qiáng)緩存到2026年∏傩恚【這個(gè)強(qiáng)緩存可以有】
使用強(qiáng)緩存后税肪,如何更新網(wǎng)站呢?
給文件加上 hash 值榜田。
知乎上的完美解決方案
https://www.zhihu.com/question/20790576
- 注意點(diǎn)【強(qiáng)緩存針對(duì)靜態(tài)資源益兄,動(dòng)態(tài)資源不要,html資源不要箭券【煌保】
強(qiáng)緩存還有一點(diǎn)需要注意的是,通常都是針對(duì)靜態(tài)資源使用辩块,動(dòng)態(tài)資源需要慎用蛔六,除了服務(wù)端頁面可以看作動(dòng)態(tài)資源外,那些引用靜態(tài)資源的html也可以看作是動(dòng)態(tài)資源庆捺,如果這種html也被緩存古今,當(dāng)這些html更新之后,可能就沒有機(jī)制能夠通知瀏覽器這些html有更新滔以,尤其是前后端分離的應(yīng)用里,頁面都是純html頁面氓拼,每個(gè)訪問地址可能都是直接訪問html頁面你画,這些頁面通常不加強(qiáng)緩存抵碟,以保證瀏覽器訪問這些頁面時(shí)始終請(qǐng)求服務(wù)器最新的資源
三、協(xié)商緩存
1. 什么是協(xié)商緩存坏匪?
如圖:返回http狀態(tài)304拟逮,Not Modified
說白了就是瀏覽器自己不確定,沒有辦法決定适滓,因此要找 服務(wù)器商量下敦迄。 服務(wù)器說可以,那瀏覽器就直接從自己緩存里面找出資源凭迹, 服務(wù)器說你這個(gè)不行啊罚屋,過期了。 我給你個(gè)新的嗅绸,瀏覽器就拿新的咯脾猛。
協(xié)商緩存要發(fā)請(qǐng)求,所有header都是 response 和 request 一人一個(gè)鱼鸠。
2. 協(xié)商緩存如何控制猛拴?
協(xié)商緩存是利用的是【Last-Modified,If-Modified-Since】和【ETag蚀狰、If-None-Match】這兩對(duì)Header來管理的愉昆。
Response Header : Last-Modified, ETag
Request Header : If-Modified-Since , If-None-Match
3. 【Last-Modified,If-Modified-Since】緩存管理方式
第一次跟web服務(wù)器請(qǐng)求資源時(shí)麻蹋,在 response 的 header 加上 Last-Modified【文件最后修改時(shí)間】
瀏覽器收到資源的時(shí)候跛溉,把資源文件和 Last-Modified 緩存起來
再次請(qǐng)求的時(shí)候, 在 Request Header 加上 If-Modified-Since 的 header哥蔚, 這個(gè)值倒谷,就是 第一次web服務(wù)器返回的 Last-Modified。
web服務(wù)器收到之后糙箍,判斷 Last-Modified-Since 和 Last-Modified 是否一致渤愁,一致則返回 304 Not Modified. 讓瀏覽器去加載緩存里面的。
不一致的話深夯,返回資源抖格,并返回一個(gè)新的 Last-Modified
瀏覽器繼續(xù)緩存下來, 然后繼續(xù)上面的步驟咕晋。
【Last-Modified雹拄,If-Modified-Since】都是根據(jù)服務(wù)器時(shí)間返回的header,一般來說掌呜,在沒有調(diào)整服務(wù)器時(shí)間和篡改客戶端緩存的情況下滓玖,這兩個(gè)header配合起來管理協(xié)商緩存是非常可靠的质蕉,但是有時(shí)候也會(huì)服務(wù)器上資源其實(shí)有變化势篡,但是最后修改時(shí)間卻沒有變化的情況翩肌,而這種問題又很不容易被定位出來,而當(dāng)這種情況出現(xiàn)的時(shí)候禁悠,就會(huì)影響協(xié)商緩存的可靠性念祭。所以就有了另外一對(duì)header來管理協(xié)商緩存,這對(duì)header就是【ETag碍侦、If-None-Match】粱坤。
4.【ETag、If-None-Match】的緩存管理的方式是
所有的步驟都是差不多的
發(fā)送請(qǐng)求瓷产,返回資源的時(shí)候站玄,也返回了一個(gè) ETag【文件的Hash值】
瀏覽器緩存資源,并緩存下 ETag
再次請(qǐng)求的時(shí)候拦英,Request Header If-None-Match 把上次傳過來的 ETag 傳過去
web服務(wù)器蜒什,在生成一個(gè)資源文件的 ETag, 然后跟傳過來的比較
一樣疤估,返回 304 Not-Modified,瀏覽器從緩存拿
不一樣灾常, web服務(wù)器返回資源,并返回一個(gè)新的 ETag铃拇, 然后重復(fù)上面操作钞瀑。
強(qiáng)緩存 和 協(xié)商緩存的 緩存管理都是一樣的步驟哈。
5. 注意ETag的使用
在分布式部署的時(shí)候慷荔,多臺(tái)機(jī)器的 Last-Modified 必須保持一致雕什,否則協(xié)商緩存會(huì)出問題。
分布式部署显晶,不同的機(jī)器生成的 ETag 都會(huì)不一樣贷岸。 然后協(xié)商緩存就會(huì)出問題×坠停【因此如果沒有搞定ETag 一致偿警,就先關(guān)閉掉∥希】
-
協(xié)商緩存 需要 配合 強(qiáng)緩存使用 【不啟動(dòng)強(qiáng)緩存螟蒸,協(xié)商緩存也就不起作用】
response header 包含了強(qiáng)緩存的管理 header
四、瀏覽器行為對(duì)緩存的影響
如果資源已經(jīng)被瀏覽器緩存下來崩掘,在緩存失效之前七嫌,再次請(qǐng)求時(shí),默認(rèn)會(huì)先檢查是否命中強(qiáng)緩存苞慢,如果強(qiáng)緩存命中則直接讀取緩存诵原,如果強(qiáng)緩存沒有命中則發(fā)請(qǐng)求到服務(wù)器檢查是否命中協(xié)商緩存,如果協(xié)商緩存命中,則告訴瀏覽器還是可以從緩存讀取皮假,否則才從服務(wù)器返回最新的資源鞋拟。這是默認(rèn)的處理方式骂维,這個(gè)方式可能被瀏覽器的行為改變:
- 當(dāng)ctrl+f5強(qiáng)制刷新網(wǎng)頁時(shí)惹资,直接從服務(wù)器加載,跳過強(qiáng)緩存和協(xié)商緩存航闺;
- 當(dāng)f5刷新網(wǎng)頁時(shí)褪测,跳過強(qiáng)緩存,但是會(huì)檢查協(xié)商緩存潦刃;
五侮措、注意點(diǎn)
勾選這個(gè) disable cache 緩存, 則不會(huì)使用緩存