快取是我們這些切圖仔繞不開的話題,但是你不理解它就會出現需要時,拿不到,不需要時,總是出來作怪,今天這篇文章將帶梳理下於快取有關的知識。
那麼什麼是快取呢?
快取是瀏覽器的一種機制,叫瀏覽器快取機制,該機制可以把一個請求過的 Web 資源(如 html 頁面,圖片,js,api資料等)拷貝一份到副本儲存在瀏覽器中,並根據請求配置選擇是否使用它們。
為什麼使用快取?
- 減少網路頻寬消耗
- 降低伺服器壓力
- 減少網路延遲,加快頁面開啟速度
瀏覽器端快取規則:對於瀏覽器的快取來講,這些規則是在 HTTP 協議頭和 HTML 頁面的 Meta 標籤中定義的。
瀏覽器的快取控制
① 使用 HTML Meta標籤
我們可以在 HTML 頁面的節點中加入標籤
<!- Pragma 是 http1.0 版本中給客戶端設定快取方式之一 ->
<meta http-equiv="Pragma" content="no-cache">
複製程式碼
上述程式碼含義是:瀏覽器當前頁面不被快取,每次訪問都向伺服器請求,但是通過這種方式去禁用快取的形式用處很有限
- 僅有 IE 才能識別這段 meta 標籤含義,其它主流瀏覽器僅識別 "Cache-Control: no-store" 的mete標籤
- 在 IE 識別到該meta標籤含義,並不一定會在請求欄位加上 Pragma,但的確會讓當前頁面每次都發新請求(僅限頁面,頁面上的資源不受影響)
② 使用快取有關的 HTTP 訊息報頭,這個後面會詳細說
與快取有關的訊息報頭有expires,cache-control,pragma,Last-Modified,If-modified-since,Etag,If-none-match 等。
瀏覽器的快取機制可分為四個方面
- Memory Cache
- Service Worker Cache
- HTTP Cache
- Push Cache
這裡我們只研究 HTTP Cache
為什麼 HTTP 快取可以提升效率呢?
我們知道,請求是個複雜的過程,需要經過
DNS解析 -> 因特網的五層協議 -> 伺服器 -> 伺服器又要進行安全校驗 -> 根據請求執行相應的程式碼 -> 封裝成 HTTP 請求 -> 四次揮手
最後我們的瀏覽器才拿到資料,而快取就是把這一系列繁瑣的過程都省略掉,當下一個請求來到時,如果是相同的 URL,直接讀取相應的資料。
如果你還不理解,這裡舉個例子,商品的總部在北京,咦,它發現廣東這邊的市場對這個產品的需求量很大,那麼它就在廣州建一個倉庫,那麼廣東這邊購買該產品時,直接從廣州發貨,大大節省了時間,提升了效率.
好了,經過上面的解釋,我們理解了,快取是一種瀏覽器的機制,它會拷貝一份副本資料儲存在本地,當遇到相同的請求,直接從本地讀取資料,通過重複利用之前獲取資源的方式來減少IO消耗,從而提高了訪問速度,所以說,快取是效能優化的一種手段。
既然快取可以提高效率,那要怎麼用呢? 不可能毫無章法吧,沒錯,確實有一套快取規則,當輸入 url,會進行一次資料請求,返回 http 響應頭,就會攜帶相應的快取規則,因此,要快取規則可以由伺服器決定。
HTTP快取有許多規則,根據是否需要重新向伺服器發起請求來分類,我將其分為兩大類(強快取和協商快取)
顧名思義,強快取意味著強制使用快取,協商快取意味著每用一次快取都要協商一次。 強快取和協商快取都允許使用情況下,優先強快取。
強快取
控制欄位:
- Expires: HTTP1.0
- Cache-Control: HTTP1.1 判斷過程:請求再次發起 -> 瀏覽器根據 expires 和 cache-control 判斷目標資源是否命中"強快取" -> 若命中,直接從快取獲取資源,不再與伺服器發生通訊。
Expires
響應頭中的expires
expires: Sat,30 Mar 2019 10:31:59 GMT
流程
- 首次請求
- 伺服器告知啟用強快取,並在響應頭中帶上 expires,告知快取到期時間,該值是個時間戳。
- 隨後的每次請求,瀏覽器會先對比本地時間和 expires 的時間戳。
- 如果本地時間小於 expires 設定的過期時間,那麼就直接去快取中取這個資源。
弊端: 依賴本地時間,如果使用者修改了本地時間,那麼 expires 就無法達到我們的預期。
基於對本地時間依賴這個弊端,HTTP1.1提出了 Cache-Control 來完全替代 expires 的任務,當兩者並存情況下,優先 Cache-Control,在當下的前端實踐裡,繼續使用 expires 的唯一目的就是向下相容
Cache-Control
響應頭中的 Cache-Control
cache-control: max-age=655350000
cache-control 中常用的值
max-age:指定的是從文件被訪問後的存活時間,這個時間是個相對值,相對的是文件第一次被請求時伺服器記錄的。
Request_time(請求時間),也就是,相對的是文件的請求時間(Atime),單位:s。
s-maxage: 表示向代理伺服器請求快取內容,只在代理伺服器中生效,優先順序高於 max-age,客戶端中只考慮 max-age。
pubilc 與 private: 針對資源是否能夠被代理伺服器快取而存在的一組對立概念,如果設定了 public,那麼它既可以被瀏覽器快取,也可以被代理伺服器快取,如果我們設定了 private,則只能被瀏覽器快取,預設值是 private。 no-cache: 每一次發起請求都不會再去詢問瀏覽器快取情況,而是直接向伺服器去確認資源是否過期(等於直接使用協商快取)。 no-store: 不使用任何快取策略,直接向服務端傳送請求,並下載完整的響應。
已存在快取資料時,僅基於強制快取,請求資料流程如下:
強快取生效的網路請求圖
狀態碼為灰色的請求則代表使用了強制快取,請求對應的Size值則代表該快取存放的位置,分別為from memory cache 和 from disk cache。協商快取
控制欄位: (HTTP1.0)
- Last-Modified:是一個時間戳,由伺服器生成,如果規則中啟用了協商快取,它會在首次請求時,隨著 Response Headers 返回,存在響應頭中。
- If-Modified-Since:也是一個時間戳,依託著 Last-Modified,是它的複製品,由瀏覽器生成,在啟用協商快取情況下,每次的請求頭中都會帶上這個欄位,它的值等於上一次響應頭返回的 last-modified 值。 (HTTP1.1)
- Etag:伺服器為每個資源生成的唯一的標識字串,這個標識字串時基於檔案內容編碼的,只要檔案內容不同,它們對應的 Etag 就是不同的,由伺服器產生,存在響應頭中。
- If-None-Match: Etag 的複製品,用法和 If-Modified-Since 一樣,存在請求頭中。
如果伺服器提示資源未改動(Not Modified),資源會被重定向到瀏覽器快取,這種情況下,網路請求對應的狀態碼是304。
Last-Modified 與 If-Modified-Since
響應頭中的 Last-Modified
last-modified: Wed,19 Dec 2018 08:57:33 GMT 請求頭中的 If-Modified-Since If-Modified-Since: Mon,14 Jan 2019 03:31:14 GMT
流程
- 首次請求
- 伺服器告知啟用協商快取規則,並在響應頭中帶上 Last-Modified,告知快取到期時間
- 隨後的每次請求,請求頭上都會攜帶 If-Modified-Since,該值等於上一次響應頭中的 Last-Modified 的值
- 伺服器收到 If-Modified-Since 後,會將該屬性的值與伺服器上資源的最後修改時間進行匹配,從而判斷資源是否發生了變化
- 如果發生變化會返回一個完整的響應內容,在響應頭中新增新的 Last-Modified 值,否則,只返回header部分,狀態碼為304,響應頭不會再新增 Last-Modified.
弊端: Last-Modified無法正確感知檔案的變化,譬如說,檔案的編輯時間修改了而內容沒有修改,或者修改檔案速度太快,幾毫秒就改一次檔案,If-Modified-Since 只能檢測秒級的變化.
為了解決這個問題,Etag 作為 Last-Modified 的升級版,因時而生
Etag 是通過標識字串來辨別檔案內容是否發生修改的,檔案內容不一致才會生成新的標識字串,這就彌補了 Last-Modified 時間戳的不足,通過 Etag 可以精準的感知檔案的變化.
Etag 與 If-None-Match
響應頭中的 Etag
etag: "2c6bee6a6ab9e39b892970e9368a3dff" 請求頭中的 If-None-Match If-None-Match: "2c6bee6a6ab9e39b892970e9368a3dff"
流程
- 首次請求
- 伺服器啟用協商快取情況下,會在響應頭中帶上 Etag
- 隨後每次請求,請求頭上都會帶上 If-None-Match,該值等於上一次響應頭中的 Etag 的值
- 伺服器收到 If-None-Match 後,會進行比對,從而判斷資源是否發生變化
- 如果變化返回一個完整響應內容,在響應頭上新增新的 Etag 值,否則返回 304,響應頭不會在新增 Etag
弊端: Etag的生成需要伺服器付出額外的開銷,會影響服務端效能
Etag 並不能替代 Last-Modified,只能作為 Last-Modified 的補充和強化存在,當 Etag 和 Last-Modified 同時出現時,以 Etag 為準
已存在快取資料時,僅基於協商快取,請求資料的流程如下
協商快取生效的網路請求圖
不能快取的請求
- HTTP資訊頭中包含Cache-Control:no-cache,pragma:no-cache(HTTP1.0),或Cache-Control:max-age=0等告訴瀏覽器不用快取的請求
- 需要根據Cookie,認證資訊等決定輸入內容的動態請求是不能被快取的
- 經過HTTPS安全加密的請求
- POST請求無法被快取
- HTTP響應頭中不包含Last-Modified/Etag,也不包含Cache-Control/Expires的請求無法被快取