本文是向大家介紹瀏覽器緩存機制,緩存是解決性能問題的重要手段,使用緩存的好處很多,除了能讓瀏覽器更快地加載網(wǎng)絡資源之外,還會帶來其他好處,比如節(jié)省網(wǎng)絡流量和帶寬,以及減少服務端的負擔。
一.什么是緩存
當我們第一次訪問網(wǎng)站的時候,電腦會把網(wǎng)站上的圖片和數(shù)據(jù)下載到電腦上,當我們再次訪問該網(wǎng)站的時候,網(wǎng)站就會從電腦中直接加載出來,這就是緩存。
緩存的好處
1. 緩解服務器壓力,不用每次都去請求某些數(shù)據(jù)了。
2.提升性能,打開本地資源肯定會比請求服務器來的快。
3.減少帶寬消耗,當我們使用緩存時,只會產(chǎn)生很小的網(wǎng)絡消耗,至于為什么打開本地資源也會產(chǎn)生網(wǎng)絡消耗,下面會有說明。
Web緩存種類
數(shù)據(jù)庫緩存,CDN緩存,代理服務器緩存,瀏覽器緩存。
1.數(shù)據(jù)庫緩存:當web應用關系復雜,數(shù)據(jù)表越來越多時,可以將查詢后的數(shù)據(jù)放到內(nèi)存中進行緩存,下次再查詢時,就直接從緩存中讀取,從而提高響應速度。
2.CDN緩存:當我們發(fā)送一個web請求的時候,cdn會幫我們計算去哪得到這些內(nèi)容的最快,所以可以將大家經(jīng)常訪問的內(nèi)容放到cdn,加快響應速度。
3.代理服務器緩存:和瀏覽器緩存性質(zhì)相似,但是代理服務器緩存面向的群體更廣,規(guī)模更大。一般為大量用戶服務,同一個副本會被應用多次,所以在減少響應時間和帶寬上很有效果
4.瀏覽器緩存:每個瀏覽器都實現(xiàn)了http緩存,我們通過瀏覽器使用http協(xié)議與服務端進行交互的時候,瀏覽器會根據(jù)一套與服務器約定的規(guī)則進行緩存工作,當我們在瀏覽器中點擊前進和后退按鈕,利用的就是瀏覽器的緩存機制。
所謂瀏覽器緩存其實就是指在本地使用的計算機中開辟一個內(nèi)存區(qū),同時也開辟一個硬盤區(qū)作為數(shù)據(jù)傳輸?shù)木彌_區(qū),然后用這個緩沖區(qū)來暫時保存用戶以前訪問過的信息。
二.緩存的過程
根據(jù)是否需要向服務器重新發(fā)起 HTTP 請求,將緩存過程分為兩個部分:強緩存和協(xié)商緩存。
強緩存:瀏覽器直接從本地緩存中獲取數(shù)據(jù),不與服務器進行交互。
協(xié)商緩存:瀏覽器發(fā)送請求到服務器,服務器判定是否可使用本地緩存。
兩種緩存方式最終使用的都是本地緩存;前者無需與服務器交互,后者需要。
1.強緩存
強制緩存是在瀏覽器加載資源的時候,先檢查緩存時間是否過期,若未過期則直接從緩存中查找請求結果,如果緩存時間過期或不存在該緩存結果,則向服務端發(fā)起請求。

設置緩存時間的方法有兩種(響應頭字段):
Expires(HTTP/1.0)
Cache-Control(HTTP/1.1)
Expires
HTTP/1.0 中使用響應頭部字段 Expires 來設置緩存過期時間??蛻舳说谝淮握埱髸r,服務端會在響應頭部添加 Expires 字段。當瀏覽器再次發(fā)送請求時,先會對比當前時間和 Expires 對應的時間,如果當前時間早于 Expires 時間,那么直接使用緩存;反之,需要再次發(fā)送請求。
expires: Sun, 24 Jul 2022 15:33:14 GMT
上述 Expires 信息告訴瀏覽器:在 2022.07.24 日之前,可以直接使用該請求的緩存。
問題:
服務端和瀏覽器的時間可能不同,導致緩存過期時間出現(xiàn)偏差
客戶端可以通過修改系統(tǒng)時間來繼續(xù)使用緩存或提前使緩存失效
為了解決這個問題,HTTP/1.1 提出了 Cache-Control 響應頭部字段。
Cache-Control
它的常用值有下面幾個:
no-cache:表示使用協(xié)商緩存,即每次使用緩存前必須向服務端確認緩存資源是否更新;
no-store:禁止瀏覽器以及所有中間緩存存儲響應內(nèi)容;
public:公有緩存,表示可以被代理服務器緩存,可以被多個用戶共享;
private:私有緩存,不能被代理服務器緩存,不可以被多個用戶共享;
max-age:以秒為單位的數(shù)值,表示緩存的有效時間;
must-revalidate:當緩存過期時,需要去服務端校驗緩存的有效性。
cache-control: max-age=31536000
此 Cache-Control 信息告訴瀏覽器該緩存為公有緩存,有效期 1 年。
* 強制緩存中,cache-control 的 max-age 優(yōu)先級高于 Expires
2.協(xié)商緩存
協(xié)商緩存不指定緩存的有效時間,而是在請求時直接發(fā)送資源標識到服務端確認緩存是否需要更新,如果請求響應返回的 HTTP 狀態(tài)為 304,則表示緩存仍然有效;否則返回狀態(tài)碼 200 、最新的資源和最新的資源標識。

資源標識(在 Response Header 中)有兩種:
Last-Modified:資源的最后修改時間
Etag:資源的唯一標識(一個字符串)
Last-Modified 和 If-Modified-Since:
服務端通過響應頭部字段 Last-Modified 和請求頭部字段 If-Modified-Since 比對雙方資源的修改時間,來確定緩存是否需要更新。具體工作流程如下:

瀏覽器第一次請求資源,服務端在返回資源的響應頭中加入 Last-Modified 字段,表示這個資源在服務端上的最近修改時間;
當瀏覽器再次向服務端請求該資源時,請求頭部帶上之前服務端返回的 Last-Modified,這個請求頭叫 If-Modified-Since;
服務端再次收到請求,根據(jù) If-Modified-Since 的值,判斷相關資源是否有變化,如果沒有,則返回 304 Not Modified,瀏覽器使用資源緩存值;否則返回資源內(nèi)容,且更新 Last-Modified 響應頭內(nèi)容。
這種方式雖然能判斷緩存是否失效,但也存在三個問題:
精度問題:Last-Modified 的時間精度為秒,如果在 1 秒內(nèi)發(fā)生修改,那么緩存判斷會失效
準度問題:如果一個文件被修改后又被還原,內(nèi)容沒有發(fā)生變化,卻仍然需要重新請求
服務器問題:某些服務器不能精確的得到文件的最后修改時間
因此我們需要 ETag
ETag 和 If-None-Match
為了解決精度問題和準度問題,HTTP 提供了另一種依賴于文件哈希值的精確判斷緩存的方式:響應頭部字段 ETag 和請求頭部字段 If-None-Match。具體工作流程如下:

瀏覽器第一次請求資源,服務端在返響應頭中加入 Etag 字段,Etag 字段值為該資源的哈希值;
當瀏覽器再次跟服務端請求這個資源時,在請求頭上加上 If-None-Match,值為之前響應頭部字段 ETag 的值;
服務端再次收到請求,將請求頭 If-None-Match 字段的值和響應資源的哈希值進行比對,如果兩個值相同,則說明資源沒有變化,返回 304 Not Modified;否則就正常返回資源內(nèi)容,無論是否發(fā)生變化,都會將計算出的哈希值放入響應頭部的 ETag 字段中。
這種緩存比較的方式也會存在一些問題,具體表現(xiàn)在以下兩個方面:
計算成本。對于大文件而言,讀取完整的文件內(nèi)容生成哈希值開銷較大;只讀取文件部分內(nèi)容,又容易判斷出錯。
計算誤差。不同服務端可能會采用不同的哈希值計算方式。所以同一個資源在兩臺服務端產(chǎn)生的 Etag 可能是不相同的。對于使用服務器集群來處理請求的網(wǎng)站來說,使用 Etag 的緩存命中率會有所降低。
兩者中會優(yōu)先使用 Etag:
Last-Modified 只能精確到秒級
如果資源被重復生成,而內(nèi)容不變,Etag 更加精準
3.總結
緩存的優(yōu)先級:
強制緩存的優(yōu)先級高于協(xié)商緩存:
強制緩存中:cache-control 的 max-age 優(yōu)先級高于 Expires
協(xié)商緩存中:Etag 優(yōu)先級比 Last-Modified 高。

用戶行為:

禁用緩存:
服務器禁用緩存:
Cache-Control: max-age=0, must-revalidate
Cache-Control: no-cache
Cache-Control: no-store
瀏覽器禁用緩存:
改變 url,加上?xi=xixi
設置請求 header
三.緩存的位置
強緩存我們會把資源放到memory cache 和 disk cache中,那什么資源放在memory cache,什么資源放在disk cache中?

圖像和網(wǎng)頁等資源主要緩存在disk cache,操作系統(tǒng)緩存文件等資源大部分都會緩存在memory cache中。具體操作瀏覽器自動分配,看誰的資源利用率不高就分給誰。
可以看到memory cache請求時間都是0ms。
查找瀏覽器緩存時會按順序查找: Service Worker-->Memory Cache-->Disk Cache-->Push Cache。
1. Service Worker
是運行在瀏覽器背后的獨立線程,一般可以用來實現(xiàn)緩存功能。使用 Service Worker的話,傳輸協(xié)議必須為 HTTPS。因為 Service Worker 中涉及到請求攔截,所以必須使用 HTTPS 協(xié)議來保障安全。Service Worker 的緩存與瀏覽器其他內(nèi)建的緩存機制不同,它可以讓我們自由控制緩存哪些文件、如何匹配緩存、如何讀取緩存,并且緩存是持續(xù)性的。
2. Memory Cache
內(nèi)存中的緩存,主要包含的是當前中頁面中已經(jīng)抓取到的資源,例如頁面上已經(jīng)下載的樣式、腳本、圖片等。讀取內(nèi)存中的數(shù)據(jù)肯定比磁盤快,內(nèi)存緩存雖然讀取高效,可是緩存持續(xù)性很短,會隨著進程的釋放而釋放。一旦我們關閉 Tab 頁面,內(nèi)存中的緩存也就被釋放了。
3. Disk Cache
存儲在硬盤中的緩存,讀取速度慢點,但是什么都能存儲到磁盤中,比之 Memory Cache 勝在容量和存儲時效性上。
在所有瀏覽器緩存中,Disk Cache 覆蓋面基本是最大的。它會根據(jù) HTTP Herder 中的字段判斷哪些資源需要緩存,哪些資源可以不請求直接使用,哪些資源已經(jīng)過期需要重新請求。并且即使在跨站點的情況下,相同地址的資源一旦被硬盤緩存下來,就不會再次去請求數(shù)據(jù)。絕大部分的緩存都來自 Disk Cache。
memory cache 要比 disk cache 快的多。舉個例子:從遠程 web 服務器直接提取訪問文件可能需要500毫秒(半秒),那么磁盤訪問可能需要10-20毫秒,而內(nèi)存訪問只需要100納秒,更高級的還有 L1緩存訪問(最快和最小的 CPU 緩存)只需要0.5納秒。
prefetch cache(預取緩存)
link標簽上帶了prefetch,再次加載會出現(xiàn)。
prefetch是預加載的一種方式,被標記為prefetch的資源,將會被瀏覽器在空閑時間加載。
4. Push Cache
Push Cache(推送緩存)是 HTTP/2 中的內(nèi)容,當以上三種緩存都沒有命中時,它才會被使用。它只在會話(Session)中存在,一旦會話結束就被釋放,并且緩存時間也很短暫,在Chrome瀏覽器中只有5分鐘左右,同時它也并非嚴格執(zhí)行HTTP頭中的緩存指令。