快取的概念分很多種,本次討論的主要就是前端快取中的Http快取。
快取是怎麼回事
前端傳送請求主要經歷以下三個過程,請求->處理->響應。 如果有多次請求就需要重複執行這個過程。
重複請求的過程
以下是一個重複請求的流程圖:
從以上的流程圖可以看書,如果使用者重複請求同一資源的話,會對伺服器資源造成浪費,伺服器重複讀取資源,傳送給瀏覽器後瀏覽器重複下載,造成不必要的等待與消耗。
快取讀取的過程
快取讀取就是瀏覽器在向伺服器請求資源之前,先查詢一下本地快取中是否存在需要的資源,如果存在,那便優先從快取中讀取。當快取不存在或者過期,再向伺服器傳送請求。
如何開啟Http快取並對快取進行設定,是本次討論的關鍵。
快取的型別
瀏覽器有如下常見的幾個欄位:
- expires: 設定快取過期的時間
- private: 客戶端可以快取
- public: 客戶端和代理伺服器都可快取
- max-age=xxx: 快取的內容將在 xxx 秒後失效
- no-cache: 需要使用對比快取來驗證快取資料
- no-store: 所有內容都不會快取,強制快取,對比快取都不會觸發
- last-modified: 內容上次被修改的時間
- Etag: 檔案的特殊標識
強制快取和協商快取
快取方法可以分為強制快取與協商快取。
從字面理解,強制快取的方式簡單粗暴,給cache
設定了過期時間,超過這個時間之後cache過期需要重新請求。上述欄位中的expires
與cache-control
中的max-age
都屬於強制快取。
協商快取根據一系列條件來判斷是否可以使用快取。
強制快取優先順序高於協商快取
強制快取
expires
expires
給瀏覽器設定了一個絕對時間,當瀏覽器時間超過這個絕對時間之後,重新向伺服器傳送請求。
Expires: Fri, 04 Jan 2019 12:00:00 GMT
這個方法簡單直接,直接設定一個絕對的時間 (當前時間+快取時間)。但是也存在隱患,例如瀏覽器當前時間是可以進行更改的,更改之後expires設定的絕對時間相對不準確,cache
可能會出現長久不過期或者很快就過期的情況。
cache-control: max-age
為了解決expires
存在的問題,Http1.1版本中提出了cache-control: max-age
,該欄位與expires
的快取思路相同,都是設定了一個過期時間,不同的是max-age
設定的是相對快取時間開始往後多久,因此不存在受日期不準確情況的影響。
但是強制快取存在一個問題,該快取方式優先順序高,如果在過期時間內快取的資源在伺服器上更新了,客服端不能及時獲取最新的資源。
協商快取
協商快取解決了無法及時獲取更新資源的問題。以下兩組欄位,都可以對資源做標識,由伺服器做分析,如果未進行更新,那返回304
狀態碼,從快取中讀取資源,否則重新請求資源。
last-modify
last-modify告知了客戶端上次修改該資源的時間,
Last-Modified: Wed, 02 Jan 2019 03:06:03 GMT
瀏覽器將這個值記錄在if-modify-since
中(瀏覽器自動記錄了該欄位資訊),下一次請求相同資源時,與伺服器返回的last-modify
進行比對,如果相等,則表示未修改,響應 304;反之,則表示修改了,響應 200 狀態碼,並返回資料。
last-modify
以秒為單位進行更新,如果小於該單位高頻進行更新的話,不適合採用該方法。
ETag
ETag是對資源的特殊標識
Etag: W/"e563df87b65299122770e0a84ada084f"
請求該資源成功之後,將返回的ETag
存入if-none-match
欄位中(瀏覽器自動記錄了該欄位資訊),同樣在請求資源時傳遞給伺服器,伺服器查詢該編碼對應的資源有無更新,無更新返回304狀態,更新返回200並重新請求。
以下有個小例子,查詢書籍更新:
當書籍資訊查詢之後,再次查詢,伺服器根據資源的ETag
查詢得知該資源沒有進行更新,返回304狀態碼。
更新返回的資料資訊,再次查詢,返回200狀態碼,重新進行請求:
從返回的Request Headers可以看出,再次請求時,瀏覽器自動傳送了If-Modified-Since
與If-None-Match
兩個欄位,瀏覽器根據這兩個欄位中(If-None-Match
優先順序大於 If-Modified-Since
)來判斷是否修改了資源。
ETag如何計算
ETag
是針對某個檔案的特殊標識,伺服器預設採用SHA256
演算法生成。也可以採用其他方式,保證編碼的唯一性即可。
快取的優先順序
根據上文優缺點的比對,可以得出以下的優先順序順序:
Cache-Control > Expires > ETag > Last-Modified
如果資源需要用到強制快取,Cache-Control
相對更加安全,協商快取中利用ETag
查詢更新更加全面。
圖片來源:瀏覽器快取機制詳解
快取儲存在哪
disk cache
disk cache
為儲存在硬碟中的快取,儲存在硬碟中的資源相對穩定,不會隨著tab或瀏覽器的關閉而消失,可以用來儲存大型的,需長久使用的資源。
當硬碟中的資源被載入時,記憶體中也儲存了該資源,當下次改資源被呼叫時,會優先從memory cache
中讀取,加快資源的獲取。
memory cache
memory cache
即儲存在記憶體中的快取,記憶體中的內容會隨著tab的關閉而釋放。
當介面狀態返回304時,資源預設儲存在memory cache
中,當頁面關閉後,重新開啟需要再次請求。
這兩種儲存方式的區別可以參考該回答
When you visit a URL in Chrome, the HTML and the other assets(like images) on the page are stored locally in a memory and a disk cache. Chrome will use the memory cache first because it is much faster, but it will also store the page in a disk cache in case you quit your browser or it crashes, because the disk cache is persistent.
當您訪問chrome中的URL時,頁面上的HTML和其他資產(如影象)將本地儲存在記憶體和磁碟快取中。Chrome將首先使用記憶體快取,因為它的速度快得多,但它也會將頁面儲存在磁碟快取中,以防您退出瀏覽器或它崩潰,因為磁碟快取是持久的。
為什麼有的資源一會from disk cache,一會from memory cache
三級快取原理
- 先去記憶體看,如果有,直接載入
- 如果記憶體沒有,擇取硬碟獲取,如果有直接載入
- 如果硬碟也沒有,那麼就進行網路請求
- 載入到的資源快取到硬碟和記憶體,下次請求可以快速從記憶體中獲取到
為什麼有的請求狀態碼返回200,有的返回304
200 from memory cache
不訪問伺服器,直接讀快取,從記憶體中讀取快取。此時的資料時快取到記憶體中的,當關閉程式後,也就是瀏覽器關閉以後,資料將不存在。
但是這種方式只能快取派生資源。
200 from disk cache
不訪問伺服器,直接讀快取,從磁碟中讀取快取,當關閉程式時,資料還是存在。
這種方式也只能快取派生資源
304 Not Modified
訪問伺服器,發現資料沒有 更新,伺服器返回此狀態碼。然後從快取中讀取資料。
薄荷應用
舉一個簡單的小?,以薄荷的減肥群頁面為討論物件,檢視一下資源載入的情況:
這些圖片都是從硬碟中讀取,因為沒有在記憶體中獲取到響應的資源,當我們重新整理頁面時,這個資源因為從硬碟中讀取時,也儲存到了記憶體中,再次獲取就是從記憶體中獲取了:
當我們沒有關閉頁面時,記憶體中的資源始終存在,重新開啟則記憶體釋放。
CDN快取
CDN邊緣節點快取策略因服務商不同而不同,但一般都會遵循http標準協議,通過http響應頭中的Cache-control: max-age的欄位來設定CDN邊緣節點資料快取時間。
當客戶端向CDN節點請求資料時,CDN節點會判斷快取資料是否過期,若快取資料並沒有過期,則直接將快取資料返回給客戶端;否則,CDN節點就會向源站發出回源請求,從源站拉取最新資料,更新本地快取,並將最新資料返回給客戶端。
如何合理應用快取
強制快取優先順序最高,並且資源的改動在快取有效期內都不會對快取產生影響,因此該方法適用於大型且不易修改的的資原始檔,例如第三方CSS、JS檔案或圖片資源,檔案後可以加上hash
進行版本的區分。建議將此類大型資源存入disk cache
,因為存在硬碟中的檔案資源不易丟失。
協商快取靈活性高,適用於資料的快取,根據上述方法的對比,採用Etag
標識進行對比靈活度最高,並考慮將資料存入記憶體中,因為記憶體載入速最快,並且資料體積小,不會佔用大量記憶體資源。
廣而告之
本文釋出於薄荷前端週刊,歡迎Watch & Star ★,轉載請註明出處。