浏览器缓存机制的研究分享
源寶導(dǎo)讀:互聯(lián)網(wǎng)Web應(yīng)用大行其道的今天,瀏覽器已經(jīng)成為Web應(yīng)用運(yùn)行的重要平臺(tái)。而Web應(yīng)用對(duì)瀏覽器緩存機(jī)制的高效利用,可以大幅提升應(yīng)用性能和用戶體驗(yàn)。本文將對(duì)瀏覽器緩存機(jī)制進(jìn)行系統(tǒng)化的梳理,分享我們的經(jīng)驗(yàn)。
一、背景
? ? 計(jì)算機(jī)讀取數(shù)據(jù)的速度逐步遞減:緩存>內(nèi)存>硬盤, 為了提高數(shù)據(jù)處理性能,其中一個(gè)策略就是對(duì)緩存的高效利用。類似的,利用瀏覽器緩存是Web應(yīng)用性能優(yōu)化的重要手段之一。優(yōu)先的緩存策略可以縮短網(wǎng)頁請(qǐng)求資源的距離,減少延遲,并且可以緩存文件進(jìn)行復(fù)用,還可以減少帶寬,降低網(wǎng)絡(luò)負(fù)荷。
? ? 對(duì)于一個(gè)數(shù)據(jù)請(qǐng)求來說,可以分為“發(fā)起請(qǐng)求”、“后端處理”、“瀏覽器響應(yīng)”3個(gè)階段,對(duì)緩存的高效利用,可以大幅提升前2步的性能。
二、對(duì)瀏覽器緩存機(jī)制的研究
2.1、緩存類型
- Memory Cache 
 memory cache 是內(nèi)存中的緩存,主要包含當(dāng)前頁面中已經(jīng)抓取到的資源。
- Disk Cache 
 disk cache 是硬盤中的緩存。
? ? 通常瀏覽器緩存分為2種:強(qiáng)緩存和協(xié)商緩存,并且都是通過設(shè)置HTTP Header實(shí)現(xiàn)的。
2.2、緩存過程
? ? 瀏覽器每次發(fā)起請(qǐng)求,都會(huì)先在瀏覽器緩存中查找請(qǐng)求的結(jié)果和標(biāo)識(shí)。瀏覽器每次拿到返回的請(qǐng)求都會(huì)將該結(jié)果和緩存標(biāo)識(shí)存入緩存中。
- 先檢查內(nèi)存,如果有,直接返回。 
- 如果內(nèi)存沒有,則去硬盤獲取,如果有直接返回。 
- 如果硬盤也沒有,那么就進(jìn)行網(wǎng)絡(luò)請(qǐng)求。 
- 加載到的資源緩存到硬盤和內(nèi)存。 
2.3、強(qiáng)緩存
? ? 強(qiáng)緩存:不會(huì)向服務(wù)器發(fā)生請(qǐng)求,直接從緩存中讀取資源。在 chrome 控制臺(tái)可以看到該請(qǐng)求返回 200 的狀態(tài)碼,并且 size 顯示 from disk cache 或 from memory cache。
? ? 強(qiáng)緩存可以通過設(shè)置HTTP Header 實(shí)現(xiàn):expires 和 Cache-Control。
- expires 
 緩存過期時(shí)間,用來指定資源到期時(shí)間,是服務(wù)器的具體的時(shí)間點(diǎn)。expires=max-age + 請(qǐng)求時(shí)間,需要和 last-modified 結(jié)合使用。expires 是 HTTP/1 的產(chǎn)物,受限于本地時(shí)間,修改本地時(shí)間,可能會(huì)導(dǎo)致緩存失效。
- cache-control 
 cache-control 可以在請(qǐng)求頭或響應(yīng)頭中設(shè)置,可以組合使用多種指令。
- public:所有內(nèi)容都會(huì)被緩存。 
- private: 所有內(nèi)容只有客戶端可以緩存。 
- no-cache: 不是說瀏覽器不使用緩存,而是先確認(rèn)下數(shù)據(jù)和服務(wù)器是否一致。也就是使用 etag 或者 last-modified 控制緩存。 
- no-store:所有內(nèi)容都不緩存,不使用強(qiáng)緩存也不使用協(xié)商緩存。 
- max-age: 表示緩存內(nèi)容在多久后失效。 
- s-maxage: 同 max-age,但是只在代理服務(wù)器生效(比如 cdn)。 
- max-stale: 能容忍的最大過期時(shí)間。 
- max-fresh: 能容忍的最小新鮮度。 
 - expires 是 HTTP 1.0 的產(chǎn)物,cache-control 是 HTTP 1.1 的產(chǎn)物,兩者同時(shí)存在時(shí),cache-control 優(yōu)先級(jí)更高。從下圖中可以發(fā)現(xiàn)強(qiáng)緩存瀏覽器加載速度超快,沒有與服務(wù)器發(fā)生交互。 
2.4、協(xié)商緩存
? ? 強(qiáng)緩存判斷緩存是否超出某個(gè)時(shí)間或者范圍,但是不關(guān)心服務(wù)器是否已經(jīng)更新內(nèi)容。為了獲取服務(wù)器更新,就需要使用協(xié)商緩存。
? ? 協(xié)商緩存就是強(qiáng)制緩存失效后,瀏覽器攜帶緩存標(biāo)識(shí)向服務(wù)器發(fā)起請(qǐng)求,由服務(wù)器決定是否使用緩存。主要有 2 種情況:
- 協(xié)商緩存生效,返回304和 no modified。 
- 協(xié)商緩存失效,返回200和請(qǐng)求結(jié)果。 
? ? 協(xié)商緩存通過 2 中 HTTP Heeader 設(shè)置:last-modified 和 etag。
2.4.1、last-modified和if-modified-since
? ? 瀏覽器在第一次訪問資源時(shí),服務(wù)器返回資源的同時(shí),在 響應(yīng)頭添加 last-modified,值是這個(gè)資源在服務(wù)器上的最后修改時(shí)間。
? ? 瀏覽器下一次請(qǐng)求,服務(wù)器檢測到 last-modified 的值,再添加個(gè) if-modified-since 值是 last-modified 的值。服務(wù)器再次收到請(qǐng)求,會(huì)根據(jù) last-modified-since 的值與服務(wù)器中這個(gè)資源的最后修改時(shí)間對(duì)比。相同 304,如果 if-modified-since 的值比服務(wù)器上資源最后修改時(shí)間小,返回新的資源和 200。
? ? last-modified 存在的弊端:
- 如果在本地打開緩存文件,即使沒有改內(nèi)容,last-modified 也會(huì)被修改,服務(wù)器就不能命中緩存。 
- last-modified 只能以秒計(jì)時(shí),如果在不可感知的時(shí)間內(nèi)修改,服務(wù)器還是會(huì)認(rèn)為命中緩存了。 
- 有的服務(wù)器文件沒有修改,可能也會(huì)更新修改時(shí)間。 
2.4.2、etag和if-none-match
? ? etag是服務(wù)器響應(yīng)請(qǐng)求時(shí),返回當(dāng)前資源文件的一個(gè)唯一標(biāo)識(shí)(由服務(wù)器生成,通常是文件的內(nèi)容md5加上修改時(shí)間算法計(jì)算得出),只要資源有變化,etag 就會(huì)重新生成。
? ? 瀏覽器下一次請(qǐng)求時(shí),只需要把上一次 etag 的值寫到 if-none-match 中,服務(wù)器再比較 if-none-match 和當(dāng)前資源的 etag 是否一致。
? ? 從下圖中可知,協(xié)商緩存即使沒有失效,也要請(qǐng)求一次服務(wù)器。
2.5、對(duì)比
? ? 精度上,etag優(yōu)于last-modified。Last-Modified的時(shí)間單位是秒,如果某個(gè)文件在1秒內(nèi)改變了多次,那么他們的Last-Modified其實(shí)并沒有體現(xiàn)出來修改,但是Etag每次都會(huì)改變確保了精度;如果是負(fù)載均衡的服務(wù)器,各個(gè)服務(wù)器生成的Last-Modified也有可能不一致。
- 在性能上,Etag肯定要遜于Last-Modified,畢竟Last-Modified只需要記錄時(shí)間,而Etag需要服務(wù)器通過算法來計(jì)算出一個(gè)hash值。 
- 在優(yōu)先級(jí)上,服務(wù)器校驗(yàn)優(yōu)先考慮Etag。 
2.6、小結(jié)
? ? 強(qiáng)緩存優(yōu)先于協(xié)商緩存,若強(qiáng)緩存生效則直接使用緩存,若不生效使用協(xié)商緩存(last-modified/if-modified-since 和 etag/if-none-match)。協(xié)商緩存由服務(wù)器決定是否使用緩存。使用緩存返回 304,不使用 返回 200 和 新的資源。用一張圖片總結(jié):
三、應(yīng)用
3.1、頻繁變動(dòng)的資源
cache-control: no-cache
通過設(shè)置 no-cache 使瀏覽器每次都請(qǐng)求服務(wù)器,然后配合使用 etag 或者 last-modified 協(xié)商緩存。
3.2、不常變化的緩存
cache-control: max-age=60480000
? ? 通過設(shè)置一個(gè)很大的過期時(shí)間,瀏覽器就會(huì)使用強(qiáng)緩存。而為了解決更新的問題,就需要在文件名(或者路徑)中添加 hash, 版本號(hào)等動(dòng)態(tài)字符,之后更改動(dòng)態(tài)字符,從而達(dá)到更改引用 URL 的目的,讓之前的強(qiáng)制緩存失效 (其實(shí)并未立即失效,只是不再使用了而已)。
3.3、SPA頁面
? ? 現(xiàn)在前端三大框架項(xiàng)目通過打包工具打包出來的資源名通常都是資源的md5值計(jì)算出來的hash,如果資源不變hash都不會(huì)變,可以讓我們有效的利用緩存。但是通常我們的靜態(tài)資源都是通過放在nginx下,而一般我們的nginx項(xiàng)目對(duì)靜態(tài)資源的緩存配置緩存了html頁面的靜態(tài)資源,大概配置如下:
location ~\.(css|js|png|jpeg|jpg|wepg|mp3|mp4|ogg|html)$ {expires 7d; }? ? 這樣會(huì)導(dǎo)致如果我們更改了文件內(nèi)容,導(dǎo)致hash變化,但是由于html還是取得緩存,這樣我們?cè)诎l(fā)版的時(shí)候,緩存還未失效的用戶可能訪問的還是舊的緩存。導(dǎo)致用戶不用第一時(shí)間看見最新版本,或者存在某些引用的資源緩存已經(jīng)失效,但是文件內(nèi)容被更改,此時(shí)資源路徑可能在服務(wù)器上已經(jīng)不存在,就會(huì)導(dǎo)致資源404。
? ? 其實(shí),通常SPA的首頁index.html一般非常小,而其它頁面都是前端路由,只是JS文件,我們針對(duì)SPA項(xiàng)目,可以不緩存html資源,針對(duì)其它文件則使用強(qiáng)緩存即可,這樣可以提高頁面加載速度。
location ~\.(html)$ {add_header Cache-Control "private, no-store, no-cache, must-revalidate, proxy-revalidate"; } location ~\.(css|js|png|jpeg|jpg|wepg|mp3|mp4|ogg)$ {expires 7d; }------ END ------
作者簡介
李同學(xué):?研發(fā)工程師,目前負(fù)責(zé)天際-移動(dòng)平臺(tái)的研發(fā)工作。
也許您還想看
記AWSS3在iOS端的一次改造事件
明源云創(chuàng)CI/CD技術(shù)演進(jìn)
微前端架構(gòu)在容器平臺(tái)的應(yīng)用
AI云店小程序演變之路
天眼探針基于rrweb實(shí)現(xiàn)前端異常視頻錄制與回放功能
總結(jié)
以上是生活随笔為你收集整理的浏览器缓存机制的研究分享的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: OneOfT1,…,Tn清新
- 下一篇: 一日一技:Ocelot网关使用Ident
