一、前言
上週阿里的面試官問了個面試題 “ 能不能說下 304 的過程,以及影響快取的頭部屬性有哪些?”OMG.......因為之前只是大概瞭解 304 狀態碼是表示快取,且因為平時專案開發過程中也沒有在快取這塊踩過坑,所以這一塊也沒有去做特別深入的研究。所以當被問這個問題時,有被當頭一棒的感覺,也好好反思了下,自己校招以軟體開發工程師的職位進入公司,之前沒有想過要從事前端開發,所以前端基礎幾乎可以忽略不計。工作一年多來,為在工作上表現突出,在工作上投入大量的精力,幹到晚上10點是常規操作,週末至少加班1天;並且空餘時間,經常看前端相關書籍彌補基礎,如《JavaScript 高階程式設計》、《CSS 權威指南》、《Sass 實踐》、《JavaScript 高效能程式設計》....《Webpack 實踐》、《深入淺出 node.js》等不下10本書;在廣度上涉及的還是很多的,但是存在問題:從事前端時間不長,前端知識雜而多,如果沒有專門準備,如果突然問你一個知識點,你雖然大概知道這是啥,但是讓你講的話,你很難有條理的講清楚。
So,下一階段的首要任務:“打好打牢前端基礎,深入瞭解所用的技術棧原理”。廢話少說,學問學問,不懂就弄清楚!以下“理論知識 + 實踐操作”來徹底弄懂 HTTP 快取機制及原理!
二、快取規則及解析
為方便大家理解,我假設覽器存在一個快取資料庫,用於儲存快取資訊。在客戶端第一次請求資料時,此時快取資料庫中沒有對應的快取資料,需要請求伺服器,伺服器返回後,將資料儲存至快取資料庫中。如下流程圖所示:
根據是否需要重新向伺服器發起請求來分類,將HTTP快取規則分為兩大類(強制快取,對比快取)在詳細介紹這兩種規則之前,先通過時序圖的方式,讓大家對這兩種規則有個簡單瞭解。
(1)已存在快取資料時,僅基於強制快取,請求資料的流程如下所示:
(2)已存在快取資料時,僅基於對比快取,請求資料的流程如下所示:
我們可以看到兩類快取規則的不同,強制快取如果生效,不需要再和伺服器發生互動,而對比快取不管是否生效,都需要與服務端發生互動。
兩類快取規則可以同時存在,強制快取優先順序高於對比快取,也就是說,當執行強制快取的規則時,如果快取生效,直接使用快取,不再執行對比快取規則。
三、快取常用欄位
1、http1.0時期的快取方案
注意:
(1)如果使用了Pragma: 'no-cache'
的話,再設定Expires
或者Cache-Control
,就沒有用了,說明Pragma
的權值比後兩者高。
(2)如果設定了Expires
之後,客戶端在需要請求資料的時候,首先會對比當前系統時間和這個Expires
時間,如果沒有超過Expires
時間,則直接讀取本地磁碟中的快取資料,不傳送請求。
2、http1.1 時期的快取方案
2.1、Cache-Control 欄位
2.1.1、Cache-Control 作為請求頭欄位
(1)Cache-Control: no-cache
使用no-cache
指令的目的是為了防止從快取中返回過期的資源。 客戶端傳送的請求中如果包含 no-cache
指令,則表示客戶端將不會接收快取的資源。每次請求都是從伺服器獲取資源,返回304。
(2)Cache-Control: no-store
使用no-store
指令表示請求的資源不會被快取,下次任何其它請求獲取該資源,還是會從伺服器獲取,返回 200,即資源本身。
2.1.2、Cache-Control 作為響應頭欄位
Cache-Control: public
當指定使用 public
指令時,則明確表明其他使用者也可利用快取。
Cache-Control: private
當指定 private
指令後,響應只以特定的使用者作為物件,這與 public
指令的行為相反。 快取伺服器會對該特定使用者提供資源快取的服務,對於其他使用者傳送 過來的請求,代理伺服器則不會返回快取。
Cache-Control: no-cache
如果伺服器返回的響應中包含 no-cache
指令,每次客戶端請求,必需先向伺服器確認其有效性,如果資源沒有更改,則返回304.
Cache-Control: no-store
不對響應的資源進行快取,即使用者下次請求還是返回 200,返回資源本身。
Cache-Control: max-age=604800(單位:秒)
資源快取在本地瀏覽器的時間,如果超過該時間,則重新向伺服器獲取。
2.2、請求頭部欄位 & 響應頭部欄位
2.2.1、請求頭部欄位
2.2.2、響應頭部欄位
注意:
(1)If-None-Match
的優先順序比If-Modified-Since
高,所以兩者同時存在時,遵從前者。
四、實驗驗證
1、實驗1 — 請求的資源沒修改,驗證2種快取出現的情形
服務端使用 node.js
, 客戶端使用 axios
進行請求:
1.1、請求頭部 / 響應頭部 設定
(1)服務端響應頭部設定:
res.setHeader('Cache-Control', 'public, max-age=10');
(2)客戶端請求頭部使用預設設定
1.2、實驗步驟
(1)請求 3 次,第一次請求請求資源;第二次在10秒內再次請求該資源,第三次在 10 秒後再次請求該資源(實驗過程中,服務端的資源沒有進行改變)
1.3、實驗結果
3 次請求的 HTTP 資訊如下圖所示,從圖中的資訊可以得出,第一次請求該資源是從伺服器獲取;第二次(10 秒內)請求該資源是直接從瀏覽器快取中獲取該資源(沒有向伺服器確認);第三次(10 秒後)請求該資源時,因為資源快取時間(10 秒)過期,所以向伺服器獲取資源,伺服器判斷該資源與本地快取的資源沒有做更改,所以返回 304,讓客戶端直接從瀏覽器快取中獲取該資源;以下,根據 HTTP 頭部資訊詳細介紹三個操作的互動過程。
1.3.1、第一次請求資源
第一次請求資源的請求頭部和響應頭部的截圖如下所示,因為第一次請求該資源,本地並沒有快取,所以直接從伺服器獲取該資源;我們從截圖可以看到,伺服器返回改資源的響應頭部中包含3個屬性與資源快取相關:
(1)cache-control: public, max-age=10
快取規則的設定,我們這個示例中,設定快取規則為 public
, 並且設定快取過期時間為10秒;
(2)etag: W/"95f15b-16994d7ebf6"
資源的唯一識別符號,客戶端下次訪問該資源時,會在請求頭中攜帶 etag
去向伺服器確認,該資源是否被修改;
(3)last-modified: Tue, 19 Mar 2019 07:26:12 GMT
資源最後一次修改時間,客戶端下次訪問該資源時,會在請求頭中攜帶該資訊去向伺服器進行匹配,該資源是否被修改;
1.3.2、第二次請求資源(10秒內,即在快取時間失效前)
第二次請求資源的請求頭部和響應頭部的截圖如下所示,因為第二次請求該資源,該資源本地快取還沒失效,所以就直接從瀏覽器快取中獲取該資源。
1.3.3、第三次請求資源(10秒後,即在快取時間失效後)
第三次請求資源的請求頭部和響應頭部的截圖如下所示,因為第三次請求該資源,該資源本地快取已經失效,所以在請求頭部中加入 If-Modified-Since
和 If-None-Match
屬性,來向伺服器進行確認該資源是否有被更改。
2、實驗2 — 請求的資源進行修改,驗證2種快取出現的情形
服務端使用 node.js
, 客戶端使用 axios
進行請求:
2.1、請求頭部 / 響應頭部 設定
(1)服務端響應頭部設定:
res.setHeader('Cache-Control', 'public, max-age=20');
(2)客戶端請求頭部使用預設設定
2.2、實驗步驟
(1)請求 3 次,第一次請求資源;然後在伺服器對請求的資源進行修改,第二次在 20 秒內再次請求該資源,第三次在 20 秒後再次請求該資源
2.3、實驗結果
3 次請求的 HTTP 資訊如下圖所示,從圖中的資訊可以得出,第一次請求該資源是從伺服器獲取;第二次(20 秒內)請求該資源是直接從瀏覽器快取中獲取該資源(沒有向伺服器確認);第三次(20 秒後)請求該資源時,因為資源快取時間(20 秒)過期,所以向伺服器獲取資源,伺服器判斷該資源與本地快取的資源不同,所以重新返回該資源;以下,根據 HTTP 頭部資訊詳細介紹三個操作的互動過程。
2.3.1、第一次請求資源
第一次請求資源的請求頭部和響應頭部的截圖如下所示,具體詳細資訊與 1.3.1 小節相同,在此不同重複介紹。
2.3.2、第二次請求資源(20 秒內,即在快取時間失效前)
第二次請求資源的請求頭部和響應頭部的截圖如下所示,(注意:即使此時伺服器上的資源已經更改,但是由於快取在瀏覽器中的資源沒有過期,所以還是從快取中返回舊資源)。
2.3.3、第三次請求資源(20 秒後,即在快取時間失效後)
第三次請求資源的請求頭部和響應頭部的截圖如下所示,因為第三次請求該資源,該資源本地快取已經失效,所以在請求頭部中加入 If-Modified-Since
和 If-None-Match
屬性,來向伺服器進行確認該資源是否有被更改,從下圖中可以看到,響應頭部的屬性 etag
與 請求頭部的屬性 If-None-Match
不同,響應頭部的屬性 If-Modified-Since
與 請求頭部的屬性 last-modified
不同;所以伺服器返回該資源的最新資源。
五、總結
1、對於強制快取,伺服器通知瀏覽器一個快取時間,在快取時間內,下次請求,直接用快取,不在時間內,執行比較快取策略。
2、對於比較快取,將快取資訊中的Etag和Last-Modified通過請求傳送給伺服器,由伺服器校驗,返回304狀態碼時,瀏覽器直接使用快取。
總結流程圖如下所示: