徹底弄懂前端快取

語言: CN / TW / HK

theme: channing-cyan highlight: a11y-light


前端快取

前端快取,這是一個老生常談的話題,也常被作為前端面試的一個知識點。今天我們再來總結一下。

分類

前端快取分為強快取和協商快取兩種。

強快取

強快取主要使用Expires、Cache-Control 兩個頭欄位,兩者同時存在Cache-Control 優先順序更高。當命中強快取的時候,客戶端不會再求,直接從快取中讀取內容,並返回HTTP狀態碼200。

  • Expires

響應頭,代表該資源的過期時間。是一個GMT 格式的標準時間。

當客戶端請求伺服器的時候,伺服器會返回資源的同時還會帶上響應頭Expires,表示資源的過期具體時間,如果客戶端在過期時間之前再次獲取該資源,就不需要再請求我伺服器了,可以直接在快取裡面拿。

使用Expires強快取優點:

  • 在過期時間以內,為使用者省了很多流量。
  • 減少了伺服器重複讀取磁碟檔案的壓力。

使用Expires強快取缺點

  • 快取過期以後,伺服器不管檔案有沒有變化會再次請求伺服器。
  • 快取過期時間是一個具體的時間,這個時間依賴於客戶端的時間,如果時間不準確或者被改動快取也會隨之受到影響。

  • Cache-Control

請求/響應頭,快取控制欄位,精確控制快取策略。

為了讓強快取更精確,HTTP1.1增加了Cache-Control欄位。Cache-Control既能出現在請求頭又能出現在響應頭,其不同的值代表不同的意思,下面我們具體分析一下。

Cache-Control 服務端引數:

  • max-age: 在多少秒內有效,是一個相對時間,這樣比Expires具體的時間就更精確了。
  • s-maxage: 就是用於表示 cache 伺服器上(比如 cache CDN,快取代理伺服器)的快取的有效時間的,並只對 public 快取有效。
  • no-cache:不使用本地強快取。需要使用快取協商。
  • no-store:直接禁止瀏覽器快取資料,每次使用者請求該資源,都會向伺服器傳送一個請求,每次都會下載完整的資源。
  • public:可以被所有的使用者快取,包括終端使用者和中間代理伺服器。
  • private:只能被終端使用者的瀏覽器快取,不允許中間快取代理進行快取,預設的。

Cache-Control 客戶端引數:

  • max-stale: 5 表示客戶端到代理伺服器上拿快取的時候,即使代理快取過期了也不要緊,只要過期時間在 5 秒之內,還是可以從代理中獲取的。
  • min-fresh: 5 表示代理快取需要一定的新鮮度,不要等到快取剛好到期再拿,一定要在到期前 5 秒之前的時間拿,否則拿不到。
  • only-if-cached 這個欄位加上後表示客戶端只會接受代理快取,而不會接受源伺服器的響應。如果代理快取無效,則直接返回 504(Gateway Timeout)。

協商快取

協商快取主要有四個頭欄位,它們兩兩組合配合使用,If-Modified-Since 和 Last-Modified一組,Etag 和 If-None-Match一組,當同時存在的時候會以Etag 和 If-None-Match為主。當命中協商快取的時候,伺服器會返回HTTP狀態碼304,讓客戶端直接從本地快取裡面讀取檔案。

  • If-Modified-Since

請求頭,資源最近修改時間,由瀏覽器告訴伺服器。其實就是第一次訪問服務端返回的Last-Modified的值。

  • Last-Modified

響應頭,資源最近修改時間,由伺服器告訴瀏覽器。

  • Etag

響應頭,資源標識,由伺服器告訴瀏覽器。

  • If-None-Match

請求頭,快取資源標識,由瀏覽器告訴伺服器。其實就是第一次訪問服務端返回的Etag的值。

If-Modified-Since 和 Last-Modified

當客戶端第一次請求伺服器的時候,服務端會返回一個Last-Modified響應頭,該欄位是一個標準時間。客戶端請求伺服器的時候會帶上If-Modified-Since請求頭欄位,該欄位的值就是伺服器返回的Last-Modified的值。伺服器接收到請求後會比較這兩個值是否一樣,一樣就返回304,讓客戶端從快取中讀取,不一樣就會返回新檔案給客戶端並更新Last-Modified響應頭欄位的值。

使用If-Modified-Since 和 Last-Modified的優點:

  • 當快取有效時伺服器不會返回檔案給客戶端,而是直接返回304狀態碼,讓客戶端從快取中獲取檔案。大大節省了流量和頻寬以及伺服器的壓力。

使用If-Modified-Since 和 Last-Modified的缺點:

  • Last-Modified 過期時間只能精確到秒。如果在同一秒既修改了檔案又獲取檔案,客戶端是獲取不到最新檔案的。

Etag 和 If-None-Match

為了解決檔案修改時間只能精確到秒帶來的問題,我們引入 Etag 響應頭。Etag 是由檔案修改時間與檔案大小計算而成,只有當檔案檔案內容或修改時間變了Etag的值才會發生變化。

當客戶端第一次請求伺服器的時候,服務端會返回一個Etag響應頭。客戶端請求伺服器的時候會帶上If-None-Match請求頭欄位,該欄位的值就是伺服器返回的Etag的值。伺服器接收到請求後會比較這兩個值是否一樣,一樣就返回304,讓客戶端從快取中讀取,不一樣就會返回新檔案給客戶端並更新Etag響應頭欄位的值。

使用Etag 和 If-None-Match的優點:

  • 當快取有效時伺服器不會返回檔案給客戶端,而是直接返回304狀態碼,讓客戶端從快取中獲取檔案。大大節省了流量和頻寬以及伺服器的壓力。
  • 並且解決了一秒內修改並讀取的問題。

擴充套件

快取失效問題

引入了快取固然是好事,能大大提升響應速度以及減輕服務端的壓力,但是也會出現一些問題,比如我們明明更新了系統版本,為什麼客戶端看到的還是老檔案。在不同的時代有不同的解決方案。

老方案

老方案通過人工自己修改檔名或者在檔名後帶上版本號、時間戳,這樣客戶端就會當新檔案請求並使用,之前的強快取就算在有效期內也會失效。

<script src="http://randy.js?version=1.1.1> </script>

新方案

在現在的構建階段基本上都不需要人工操作了,都是使用構建工具比如Wbpack、Gulp、Grunt等構建工具自動構建。比如在使用Webpack構建的時候,會根據檔名或檔案內容自動計算hash值來給檔案命名,當內容或檔名發生改變的時候,構建出來的檔名也一定會不一樣,這樣也解決了強快取還在有效期內的問題。

pragma

pragma是舊產物,已經逐步拋棄,有些網站為了向下相容還保留了這個欄位。pragma的值為no-cache時,表示禁用快取。優先順序是 pragma > cache-control > expires。

流程圖

有了這張流程圖,可以讓你們理解的更清楚。

快取的配置

如果我們使用Nginx作為Web伺服器,我們可以如下配置

``` location / {

# 其它配置 ...

if ($request_uri ~ ..$) { #非html快取1個月 add_header Cache-Control "public, max-age=2592000"; }

if ($request_filename ~ ^..$) { #html檔案使用協商快取 add_header Cache-Control "public, no-cache"; } }

```

後記

本文為筆者個人學習筆記,如有謬誤,還請告知,萬分感謝!如果本文對你有所幫助,還請點個贊~~