[故事] 瀏覽器快取原理你真的懂嗎?一篇漫畫故事帶你理解透頂!

程式設計師二毛發表於2020-04-12


本文知識結構:


今天二毛下了個晚班,坐在異鄉寥寥無幾人的地鐵上,一種“他鄉安置不了靈魂,故鄉放置不了肉身”的感覺襲來,心情有些許低落。

他似玩非玩著手機,突然被幾條資訊吸引了注意力:“熱乾麵回來了”、“全國絕大部分省宣佈開學了”…

二毛突然心情釋懷,想起一句話:冬天已經過去,春天還會遠嗎?一切都在慢慢變好,包括自己。

回到出租屋,二毛突然看到二丫一臉生氣的樣子經過,於是湊上前去關心慰問。

1 HTTP協議的組成部分

開始之前,首先我們需要大概瞭解下 HTTP 協議傳輸資訊的兩個組成部分:請求和響應。

1.1 請求

分為:請求行,請求頭,請求體。

當客戶端向服務端發起請求時,內容層次如下:

其中請求體是伺服器真正要接收處理的使用者資料;請求行和請求頭的一些引數值,主要是用於告訴伺服器如何處理這次請求,比如:

  • 根據content-length的值,伺服器就知道請求體的長度

  • 根據host的值,伺服器就知道要將請求轉發給哪個虛擬主機

  • ……

1.2 響應

分為:響應行,響應頭,響應體。

當服務端響應資源給客戶端時,內容類別如下:

其中響應體是真正展示給使用者的內容(一般是html,交給瀏覽器渲染);響應行和響應頭的一些引數值,主要告訴瀏覽器如何展示響應體的內容,比如:

  • 根據content-type的值,瀏覽器就知道是將這些內容按圖片格式展示,還是以文字方式形式

  • 根據content-length的值,瀏覽器就知道響應內容的長度

  • ……

1.3 快取相關的引數

我們主要挑快取相關的引數來講:狀態碼,頭部欄位。

1.3.1 狀態碼

這裡指的就是服務端的響應狀態碼,狀態碼主要分為以下五種:

其中,重要的快取相關狀態碼如下:

200 OK: 代表資源被瀏覽器成功接收,或資源直接從本地獲取成功。

304 Not Modified:代表服務端允許請求訪問資源,但訪問資源未滿足條件,不需要將資源響應返回給客戶端。

1.3.2 頭部欄位

認真閱讀的朋友們可能會發覺到,上面請求頭和響應頭的引數怎麼有些是一樣的(上文的content-type)。的確,其實請求頭和響應頭都叫做頭部欄位。頭部欄位分為以下四種:

1 請求首部欄位(Request Header Fields)

從客戶端向伺服器端傳送請求報文時使用的首部。

2 響應首部欄位(Response Header Fields)

從伺服器端向客戶端返回響應報文時使用的首部。

3 通用首部欄位(General Header Fields)

請求報文和響應報文兩方都會使用的首部。

4 實體首部欄位(Entity Header Fields)

針對請求報文和響應報文的實體部分使用的首部。補充了資源內容更新時間等與實體有關的資訊。

關於快取,我們需要關注這幾種頭部資訊:

  • 請求相關:If-Modified-Since,If-None-Match

  • 響應相關:ETag,Last-Modified,Expires,Cache-Control

2 瀏覽器快取型別

正如我前面說到的,瀏覽器快取有兩種,分別是:強快取和協商快取。

2.1 強快取

定義:

顧名思義,就是瀏覽器強制使用本地快取來作為資源展示。

在谷歌瀏覽器按 F12 之後的 network 選項中可以看出:命中強快取的請求,會得到200狀態碼,由於欄位是從本地獲取的,所以size 欄位處會顯示 from memory cache 或 from disk cache。

強快取相關的頭部資訊:

看響應頭部欄位:Expires 和 Cache-Control。

Expires: 例如【expires: Sun, 03 May 2020 15:02:48 GMT】,標明瞭這個資源的過期時間,瀏覽器下一次請求時會對這個時間進行判斷,如果沒超過這個時間就會命中強快取,超過了則沒命中並重新發起請求。

Cache-Control: 例如【cache-control: max-age=2592000】,標明瞭這個資源的過期倒數計時,這個倒數計時瀏覽器也會儲存下來,瀏覽器下一次請求時會對倒數計時進行判斷,如果倒數計時未完就會命中強快取,超過了則反之。

2.2 協商快取

定義

顧名思義,就是要跟伺服器進行協商要不要用快取。

在谷歌瀏覽器按 F12 之後的 network 選項中可以看出:命中協商快取的請求,會得到304狀態碼,然後瀏覽器就會從本地快取中讀取資源。

協商快取相關的頭部資訊

既然是協商,所以此類的頭部資訊是成雙成對出現的:

  • 請求頭的 ETag 與 響應頭的 If-None-Match

  • 請求頭的 Last-Modified 與 響應頭的If-Modified-Since

ETag和If-None-Match:

Etag是上一次載入資源時,伺服器返回的響應頭,是對該資源的一種唯一標識,只要資源有變化,Etag就會重新生成。瀏覽器在下一次載入資源向伺服器傳送請求時,會將上一次返回的Etag值放到請求頭裡的If-None-Match欄位裡,伺服器接受到If-None-Match的值後,會拿來跟該資原始檔的Etag值做比較,如果相同,則表示資原始檔沒有發生改變,命中協商快取。

Last-Modified和If-Modified-Since:

Last-Modified是該資原始檔最後一次更改時間,伺服器會在響應頭裡返回,同時瀏覽器會將這個值儲存起來,在下一次傳送請求時,放到請求頭裡的If-Modified-Since裡,伺服器在接收到後也會做比對,如果相同則命中協商快取。

舉個例子。

響應頭:

請求頭:

ETag和Last-Modified的作用和用法也是差不多,說一說他們的區別。

1 在精確度上,Etag要優於Last-Modified。Last-Modified的時間單位是秒,如果某個檔案在1秒內改變了多次,那麼他們的Last-Modified其實並沒有體現出來修改,但是Etag每次都會改變確保了精度;如果是負載均衡的伺服器,各個伺服器生成的Last-Modified也有可能不一致。

2 在效能上,Etag要遜於Last-Modified,畢竟Last-Modified只需要記錄時間,而Etag需要伺服器通過演算法來計算出一個hash值。

3 在優先順序上,伺服器校驗優先考慮Etag。

3 瀏覽器快取過程示意圖

注意:

  • 兩者的共同點是,都是從客戶端快取中讀取資源;區別是強快取不會發請求,協商快取會發請求。

  • 關於優先順序:先判斷強快取,後判斷協商快取。

  • 判斷強快取的時候,如果有 Cache-Control ,則只通過 Cache-Control 來判斷是否有強快取;如果沒有這個 Cache-Control 這個欄位,** 才拿 ** Expire 欄位進行判斷。

  • 判斷協商快取的時候,如果有 If-None-Match ,則只通過If-None-Match 來判斷是否有協商快取;如果沒有 If-None-Match 這個欄位,** 才拿 ** If-Modified-Since。

  • https 也是同樣的快取原理,因為 https 只是在 http 的基礎上套了SSL/TLS的加密層,其他不變。

4 針對快取的一些使用者操作

  • 位址列訪問,連結跳轉是正常使用者行為,將會觸發瀏覽器快取機制;

  • F5重新整理,瀏覽器會設定max-age=0,跳過強快取判斷,會進行協商快取判斷;

  • ctrl+F5重新整理,跳過強快取和協商快取,直接從伺服器拉取資源。

二丫隨即拿出筆記本按了下 ctrl+F5 ,果不其然,網站系統正常更新了圖片。

我是二毛,一個在大城市漂泊的程式猿。

我的故事未完待續……


關注公眾號《程式設計師二毛》,後臺回覆 1024 領取變強祕籍;

本作品採用《CC 協議》,轉載必須註明作者和本文連結

二毛

相關文章