真的只是簡單瞭解下瀏覽器快取

安木夕發表於2023-04-25

image.png

01、什麼是HTTP快取,如何工作的?

當我們開啟一個頁面時,會向服務端發起很多次請求,如下圖開啟百毒首頁,發起了HTML、各種圖片、JS、CSS等資源共72次請求。這裡面很多資源並不會頻繁變化,每次開啟頁面都重新請求下載,就很浪費了。

image.png

瀏覽器快取也稱為HTTP快取,HTTP快取簡單理解就是本地(瀏覽器)快取了HTTP響應,以便後續複用,減少向服務端的請求。尤其對於一些靜態資源,如圖片、JS檔案、CSS檔案等資源,瀏覽器端快取可以極大提升頁面的載入速度,不用每次都都全部從服務端下載,也減輕了服務端的壓力。

如下圖,再次開啟百度首頁,可以看到很多圖片、JS資源、CSS資源都是從快取載入的,網頁載入飛快。

image.png

?快取是一個很重要、很常用的技術,在IT系統中無處不在,基本原理都是就近儲存可能要用的資料,用空間換時間,提升資料的載入效率。

  • 前端瀏覽器快取HTTP的響應。
  • 後端服務的資料API也會有快取,避免每次都訪問資料庫,資料庫本身也會有快取。
  • CDN內容分發也是一種分散式的快取。
  • 計算機中的CPU、磁碟也都會有自己的快取機制。

1.1、基於URL快取資源

URL是定位資源的唯一路徑,通常來說快取也是基於URL地址的。但不僅限於URL,也支援以透過Header的欄位Accept、Accept-Language 、 Accept-Encoding標記的不同內容進行快取,可透過Vary欄位來申明(/ˈveri/ 改變)。

Vary: Accept-Language

1.2、存在什麼地方?

主要儲存在記憶體、磁碟上,具體存在哪則由瀏覽器來決定了,比如大檔案可能會存在磁碟上,使用很頻繁的資源可能會儲存在記憶體中,或者都會儲存。

image.png

  • 記憶體 from memory cache:記憶體讀取速度快,程式結束記憶體就釋放了。
  • 磁碟 from disk cache:讀取速度稍慢(其實也是很快的),可以長久儲存。

1.3、快取規則

基本的快取讀取規則都是先從本地瀏覽器快取查詢,如果有且資源沒有過期就用本地的,否則求去服務端請求。具體過程又分為強快取、協商快取:

  • 第一次請求:本地沒有,訪問伺服器獲取響應,瀏覽器會快取響應。
  • 第二/N次請求:本地有快取,快取內容有效(沒有過保質期),則響應狀態碼為200,直接使用本地快取。—— 這個過程中,直接使用了本地的快取內容稱為“強快取”。
  • 第N次請求:本地有快取,但快取內容已過期,則會向服務端發起請求詢問該資源是否有變化,服務端判斷如果資源沒變化,則返回304(資源沒變化,可繼續食用,沒有body內容),瀏覽器使用本地快取內容(並更新有效期)。如果服務端判斷資源變了,則返回狀態碼200 + 新的資源,瀏覽器收到會更新快取。—— 這個過程中,瀏覽器詢問了服務端資源是否有效,進行了協商,稱為“協商快取”。

image

所以快取的使用過程就是,先強快取(本地快取有效),再協商快取。

304: Not Modified 資源未修改,客戶端快取了資源,重定向到本地。


02、強快取:本地快取

?強快取:瀏覽器自行決定的快取處理機制,不需連線服務端。瀏覽器在請求資源時,先在瀏覽器快取中查詢,如果找到,且快取有效期正常,則強快取生效,直接從快取中獲取響應。否則就會使用協商快取,向服務端發起請求。

所以這裡關鍵就是判斷快取是否有效,是否在有效期內。關鍵(Header)欄位就是:

  • Expires(HTTP/1.0):過期時間。
  • Cache-Control:max-age(HTTP/1.1):快取有效時長。

?都是服務端響應時,在Header上標記的值,瀏覽器根據這個欄位值來判等資源是否有效。Cache-Control:max-age優先順序更高,也更好用。

2.1、Expires過期時間

HTTP/1.0的技術(已過時),在Header中使用,規定快取的具體有效期(年月日時分秒),在此期限內則快取有效。

Expires: Tue, 28 Feb 2022 22:22:22 GMT

缺點是:值的解析和計算不方便,而且依賴於本地的時間,容易被篡改。

2.2、Cache-Control:max-age

Cache-Control 是HTTP/1.1中的一個Header欄位,用來實現快取的各種配置。常用指令max-age設定快取的最大有效時長,用來替換Expires,如果同時存在則max-age優先。

HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1024
Date: Tue, 22 Feb 2022 22:22:22 GMT
Cache-Control: max-age=3600		//3600秒,表示一個小時內有效

Cache-Control常用指令:

指令 說明
max-age 快取最大週期 (單位秒),超過這個時間快取被認為過期
no-cache 強制重新驗證,不會直接使用本地快取,必須每次都詢問伺服器,就是後文的協商快取
no-store 不快取,禁用快取
public 都可以快取的公共資源,客戶端、代理伺服器都可以快取
private 個性化的、私有化內容,每個使用者請求URL相同但的內容不同的資源,只能客戶端快取,代理伺服器不可快取
must-revalidate 過期後必須重新驗證(revalidate /riˈvælɪˌdet/ 重新驗證)
immutable 長時間快取(/ɪˈmjuːtəb(ə)l/ 永恆的)
Cache-Control: private
Cache-Control: public, max-age=31536000  //1年有效期
Cache-Control: max-age=31536000, immutable
Cache-Control: max-age=0, must-revalidate  //立即失效,必須重新驗證(協商快取)

如果沒有設定Cache-Control欄位,採用“啟發式快取“,儘量的最大限度的使用快取機制。


03、協商快取:服務端驗證

?協商快取:瀏覽器需要連線服務端,向服務端詢問資源是否過期,服務端判斷沒過期就繼續用本地快取,否則返回新的資源。

✔️基本過程都是

  • 請求HTTP資源時,先查詢快取,找到了但快取過期,或快取設定了no-cache,開始“協商快取”。
  • 發起一個HTTP請求,向服務端詢問資源是否過期。
  • 服務端收到請求後驗證資源是否變更,若沒變更則返回304(沒有body,所以速度很快),告訴瀏覽器資源沒有修改,可以繼續使用本地快取。否則返回200+新的資源。

✔️協商快取的關鍵(Header)欄位為

  • last-modified:資源最後修改時間。
  • ETag:資源的唯一編碼,檔案內容變更後更新。

✔️觸發協商快取的條件

  • Cache-Control:no-cache :強制使用協商快取。
  • 本地快取過期,或Cache-Control: max-age=0

3.1、last-modified 最後修改時間

last-modified 最後修改時間,服務端在Header上給出的資源最後修改時間。

  • 第一次請求資源時,服務端會在響應Header中會返回last-modified欄位。
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1024
Date: Tue, 22 Feb 2022 22:22:22 GMT
Last-Modified: Tue, 22 Feb 2022 22:00:00 GMT
Cache-Control: max-age=3600  //快取有效期1個小時
  • 再次請求該資源時,瀏覽器發現快取資源已過期(時間已超過1個小時),開始“協商快取”。
  • 先發起一個(詢問)HTTP請求,Header中加上if-modified-since(/ˈmɑːdɪfaɪd/ /sɪns/),值為上次的last-modified值。
GET /index.html HTTP/1.1
Host: example.com
Accept: text/html
If-Modified-Since: Tue, 22 Feb 2022 22:00:00 GMT
  • 服務端收到請求,判斷如果資源最後修改時間相同,認為資源沒有變化,可以繼續使用本地的快取,則返回304(無body內容)。否則返回200+新的資源,並更新last-modified
  • 瀏覽器收到304響應後,從本地快取獲取資源,並恢復其正常快取狀態,更新Last-Modified,生命延遲1小時。
HTTP/1.1 304 Not Modified
Content-Type: text/html
Date: Tue, 22 Feb 2022 23:22:22 GMT
Last-Modified: Tue, 22 Feb 2022 22:00:00 GMT
Cache-Control: max-age=3600
  • 如果瀏覽器收到200響應,同第一次請求資源一樣,更新快取資源。

image

⛔缺點:如果1s內有多次檔案修改,則會判斷出現問題,因此就有了下面的ETag

3.2、ETag 資源唯一標籤

ETag,機制和last-modified類似,ETag是檔案內容的一個唯一識別符號,檔案變更後會更新識別符號,可以看做是檔案內容的摘要。值是服務端自定義生成的,目的是為了標記檔案內容的版本,可以用版本號、雜湊值等。

  • 第一次請求資源時,服務端在響應Header中會返回ETag欄位。
  • 協商快取時傳送HTTP請求,瀏覽器在Header中加上If-None-Match,值為上一次的ETag值。
  • 服務端收到請求,讀取If-None-Match值,判斷如果匹配一致,檔案沒更新,則返回304。否則返回200+新的資源,並更新ETag
  • 瀏覽器收到304響應後,從本地快取獲取資源,並恢復其正常快取狀態,更新ETag
  • 如果瀏覽器收到200響應,同第一次請求資源一樣,更新快取資源。
HTTP/1.1 200 OK
Accept-Ranges: bytes
Cache-Control: max-age=0
Connection: keep-alive
Content-Length: 114
Content-Type: text/html
Date: Wed, 19 Apr 2023 09:05:30 GMT
Etag: "639b0692-72"
Expires: Wed, 19 Apr 2023 09:05:30 GMT   //2023年4月19日09:08,今天早上,現在是下午5點多了。
Last-Modified: Thu, 15 Dec 2022 11:35:46 GMT

?相比較而言,ETag要更科學、更準確一些。檔案修改了並不程式碼內容一定有變化,還有就是最後修改時間的單位是秒,如果1秒內的多次變化就會判斷失誤。Etag 優先順序高於 Last-Modified。


04、總結一下-流程圖

畫圖!

image


05、一些實踐

5.1、快取應用

  • 不經常變的靜態資源,如JS、CSS、圖片推薦使用強制快取,Cache-Control: max-age=31536000
  • HTML文件推薦使用協商快取,比如SPA的入口頁面可以使用Cache-Control: no-cache強制每次都使用協商快取。
  • Last-Modified、ETag可同時使用,ETag優先順序更高,但Last-Modified還有額外的價值,可用來標識檔案的修改時間。
  • 合理定製不同資原始檔的快取機制、快取週期。
  • 快取的Header配置都是在服務端,可以透過Nginx配置。

5.2、快取破壞

快取破壞:每次內容變化時都更新URL地址,一般就是更新檔名,或檔名附加版本號。大多WEB專案的JS、CSS檔案都是這麼幹的,每次編譯的時候都會更新檔名。

# version in filename
bundle.v123.js

# version in query
bundle.js?v=123

# hash in filename
bundle.YsAIAAAA-QG4G6kCMAMBAAAAAAAoK.js

# hash in query
bundle.js?v=YsAIAAAA-QG4G6kCMAMBAAAAAAAoK

5.3、如何清除快取?

  • 瀏覽器強制(忽略快取)重新整理:Shift + F5Command + Shift + R(Mac)。
  • 清除瀏覽器快取:設定 > 歷史記錄 > 清除瀏覽資料。
  • 瀏覽器禁用快取:瀏覽器除錯模式下 > 網路 > 開啟“禁用快取”。

image.png


參考資料


©️版權申明:版權所有@安木夕,本文內容僅供學習,歡迎指正、交流,轉載請註明出處!原文編輯地址-語雀

相關文章