瀏覽器的緩存機(jī)制主要有三部分:
- 強(qiáng)緩存
- 協(xié)商緩存
- 緩存位置
強(qiáng)緩存
通過相應(yīng)的字段來檢查是否有強(qiáng)緩存
在 HTTP/1.0 中,使用的是Expires
Expires即過期時(shí)間,存在于服務(wù)端返回的響應(yīng)頭中,告訴瀏覽器在這個(gè)過期時(shí)間之前可以直接從緩存里面獲取數(shù)據(jù),無需再次請(qǐng)求。比如下面這樣:
Expires: Wed, 22 Nov 2019 08:41:00 GMT
表示資源在2019年11月22號(hào)8點(diǎn)41分過期,過期了就得向服務(wù)端發(fā)請(qǐng)求。
缺點(diǎn):服務(wù)器的時(shí)間和瀏覽器的時(shí)間可能并不一致,那服務(wù)器返回的這個(gè)過期時(shí)間可能就是不準(zhǔn)確的。因此這種方式很快在后來的HTTP1.1版本中被拋棄了。
在HTTP/1.1使用的是Cache-Control
采用過期時(shí)長(zhǎng)來控制緩存,對(duì)應(yīng)的字段是max-age。比如這個(gè)例子:
Cache-Control:max-age=3600
代表這個(gè)響應(yīng)返回后在 3600 秒,也就是一個(gè)小時(shí)之內(nèi)可以直接使用緩存。
可以組合多種指令:
-
public: 客戶端和代理服務(wù)器都可以緩存。因?yàn)橐粋€(gè)請(qǐng)求可能要經(jīng)過不同的代理服務(wù)器最后才到達(dá)目標(biāo)服務(wù)器,那么結(jié)果就是不僅僅瀏覽器可以緩存數(shù)據(jù),中間的任何代理節(jié)點(diǎn)都可以進(jìn)行緩存。 -
private: 這種情況就是只有瀏覽器能緩存了,中間的代理服務(wù)器不能緩存。 -
no-cache: 跳過當(dāng)前的強(qiáng)緩存,發(fā)送HTTP請(qǐng)求,即直接進(jìn)入?yún)f(xié)商緩存階段。 -
no-store:非常粗暴,不進(jìn)行任何形式的緩存。 -
s-maxage:這和max-age長(zhǎng)得比較像,但是區(qū)別在于s-maxage是針對(duì)代理服務(wù)器的緩存時(shí)間。
當(dāng)Expires和Cache-Control同時(shí)存在的時(shí)候,Cache-Control會(huì)優(yōu)先考慮。
協(xié)商緩存
強(qiáng)緩存失效之后,瀏覽器在請(qǐng)求頭中攜帶相應(yīng)的緩存tag來向服務(wù)器發(fā)請(qǐng)求,由服務(wù)器根據(jù)這個(gè)tag,來決定是否使用緩存,這就是協(xié)商緩存。
具體來說,這樣的緩存tag分為兩種: Last-Modified 和 ETag。這兩者各有優(yōu)劣。
Last-Modified
即最后修改時(shí)間。在瀏覽器第一次給服務(wù)器發(fā)送請(qǐng)求后,服務(wù)器會(huì)在響應(yīng)頭中加上這個(gè)字段。
瀏覽器接收到后,如果再次請(qǐng)求,會(huì)在請(qǐng)求頭中攜帶If-Modified-Since字段,這個(gè)字段的值也就是服務(wù)器傳來的最后修改時(shí)間。
服務(wù)器拿到請(qǐng)求頭中的If-Modified-Since的字段后,其實(shí)會(huì)和這個(gè)服務(wù)器中該資源的最后修改時(shí)間對(duì)比:
- 如果請(qǐng)求頭中的這個(gè)值小于最后修改時(shí)間,說明是時(shí)候更新了。返回新的資源,跟常規(guī)的HTTP請(qǐng)求響應(yīng)的流程一樣。
- 否則返回304,告訴瀏覽器直接用緩存。
ETag
ETag 是服務(wù)器根據(jù)當(dāng)前文件的內(nèi)容,給文件生成的唯一標(biāo)識(shí),只要里面的內(nèi)容有改動(dòng),這個(gè)值就會(huì)變。服務(wù)器通過響應(yīng)頭把這個(gè)值給瀏覽器。
瀏覽器接收到ETag的值,會(huì)在下次請(qǐng)求時(shí),將這個(gè)值作為If-None-Match這個(gè)字段的內(nèi)容,并放到請(qǐng)求頭中,然后發(fā)給服務(wù)器。
服務(wù)器接收到If-None-Match后,會(huì)跟服務(wù)器上該資源的ETag進(jìn)行比對(duì):
- 如果兩者不一樣,說明要更新了。返回新的資源,跟常規(guī)的HTTP請(qǐng)求響應(yīng)的流程一樣。
- 否則返回304,告訴瀏覽器直接用緩存。
兩者對(duì)比
- 在精準(zhǔn)度上,
ETag優(yōu)于Last-Modified。由于ETag是按照內(nèi)容給資源上標(biāo)識(shí),因此能準(zhǔn)確感知資源的變化。而Last-Modified就不一樣了,它在一些特殊的情況并不能準(zhǔn)確感知資源變化,主要有兩種情況:
編輯了資源文件,但是文件內(nèi)容并沒有更改,這樣也會(huì)造成緩存失效。
Last-Modified能夠感知的單位時(shí)間是秒,如果文件在 1 秒內(nèi)改變了多次,那么這時(shí)候的Last-Modified并沒有體現(xiàn)出修改了。
- 在性能上,
Last-Modified優(yōu)于ETag,也很簡(jiǎn)單理解,Last-Modified僅僅只是記錄一個(gè)時(shí)間點(diǎn),而Etag需要根據(jù)文件的具體內(nèi)容生成哈希值。
另外,如果兩種方式都支持的話,服務(wù)器會(huì)優(yōu)先考慮ETag。
緩存位置
當(dāng)強(qiáng)緩存命中或者協(xié)商緩存中服務(wù)器返回304的時(shí)候,我們直接從緩存中獲取資源。那這些資源究竟緩存在什么位置呢?
瀏覽器中的緩存位置一共有四種,按優(yōu)先級(jí)從高到低排列分別是:
- Service Worker
- Memory Cache
- Disk Cache
- Push Cache
Service Worker
Service Worker 借鑒了 Web Worker的 思路,即讓 JS 運(yùn)行在主線程之外,由于它脫離了瀏覽器的窗體,因此無法直接訪問DOM。雖然如此,但它仍然能幫助我們完成很多有用的功能,比如離線緩存、消息推送和網(wǎng)絡(luò)代理等功能。其中的離線緩存就是 Service Worker Cache。
Memory Cache 和 Disk Cache
Memory Cache指的是內(nèi)存緩存,從效率上講它是最快的。但是從存活時(shí)間來講又是最短的,當(dāng)渲染進(jìn)程結(jié)束后,內(nèi)存緩存也就不存在了。Disk Cache就是存儲(chǔ)在磁盤中的緩存,從存取效率上講是比內(nèi)存緩存慢的,但是他的優(yōu)勢(shì)在于存儲(chǔ)容量和存儲(chǔ)時(shí)長(zhǎng)。
瀏覽器如何決定將資源放進(jìn)內(nèi)存還是硬盤呢?主要策略如下:
- 比較大的JS、CSS文件會(huì)直接被丟進(jìn)磁盤,反之丟進(jìn)內(nèi)存
- 內(nèi)存使用率比較高的時(shí)候,文件優(yōu)先進(jìn)入磁盤
Push Cache
即推送緩存,這是瀏覽器緩存的最后一道防線。它是 HTTP/2 中的內(nèi)容,雖然現(xiàn)在應(yīng)用的并不廣泛,但隨著 HTTP/2 的推廣,它的應(yīng)用越來越廣泛。
總結(jié)
首先通過 Cache-Control 驗(yàn)證強(qiáng)緩存是否可用
如果強(qiáng)緩存可用,直接使用
-
否則進(jìn)入?yún)f(xié)商緩存,即發(fā)送 HTTP 請(qǐng)求,服務(wù)器通過請(qǐng)求頭中的
If-Modified-Since或者If-None-Match字段檢查資源是否更新- 若資源更新,返回資源和200狀態(tài)碼
- 否則,返回304,告訴瀏覽器直接從緩存獲取資源