前言
做為一個正宗的科班出身, 《計算機網路》 這門課程肯定是學過的,但到底有沒有學細學透學明白,就不得而知了。
依舊是做為一個正宗的科班出身偽前端,在新的網際網路寒冬之下又被某網際網路公司的面試官噴了。
雖然曾經還是比較仔細的看過 HTTP 相關的東西,但長時間沒有複習加上當時沒有做筆記、寫部落格沉澱還是有很多的生疏。
回去之後查了一下相關的資料,算是做一次知識的沉澱(造輪子)。
正文
1、 304
開啟 Chrome
,按下 F12
、訪問一個曾經訪問過的網頁,如果沒有勾選 Disable cache
,很容易能看到這樣的狀態:
304,代表 NOT MODIFIED
,他發生在這樣的一種狀態下:伺服器正確接收到了一個帶條件(Conditional Validation)的 GET
,如果這個條件是真的就會返回 304
、否則就會返回 200
(A conditional GET or HEAD request has been received and would have resulted in a 200 OK response if it were not for the fact that the condition evaluated to false)。
換個角度來說,如果瀏覽器接收到的 response
的狀態碼是 304
,就代表這個資源在客戶端中的快取依然是有效的,即在上次拿到資源到當前這段時間之內伺服器端並沒有對這個資源做修改。
這樣做有什麼好處呢? 顯然這樣可以在使用很小的一個 HTTP
請求的代價上就實現下面兩個功能:
- 確保拿到的資源是最新的
- 客戶端資源沒有問題的情況下不需要再次那資源,解決每次完整請求資源時的效能問題。 撒
那所謂的帶提交的 GET
是怎麼回事呢? 請看下面的內容:
2、 撲所迷離的 etag 與 last-modified
a、曖昧期的表白
在大多數的情況下大家聽到的可能就是 etag
與 last-modified
,沒錯,他就是伺服器在接收到帶條件的GET
請求之後塞到 response
的 header
裡面。那條件是什麼?是不是又與 etag
和 last-modified
相對應呢?
答案是肯定的,他們的對應如下所示:
etag —— if-none-match
last-modified —— if-modified-since
b、前戲
有了上面的對應關係之後相信不再撲朔了。他們的關係如下:
如果本地有相關資源的快取,並且在快取的時候響應頭裡面有 etag
或者 last-modified
的情況,這個時候去請求伺服器的時候就會是帶有條件的 GET
請求(Conditional Validation)。
在請求頭裡面可能會有兩個欄位: if-none-match
、 if-modified-since
,其中 if-none-match
的值是伺服器上次返回該資源時響應頭裡面 etag
的值,if-modified-since
的值是伺服器上次返回該資源時響應頭裡面 last-modified
裡的值。
緊接著伺服器端就會接收到這個帶有條件的 request
,然後會根據這兩個值去判斷快取的資源是否是最新的。
如果沒問題,即資源是最新的情況下就會返回 304
,body
為空;不是的話就會返回 200
,即目前瀏覽器端的資源不是最新的,body
裡面就是資源體,然後客戶端就會用最新返回的資源覆蓋掉之前的資源
也就是說。傳送這種帶條件的請求的必要條件是 資源在瀏覽器端有快取,並且在快取的時候伺服器端的reponse
裡面有 etag
或者 last-modified
。如果這個條件不滿足,傳送的請求就是沒有條件的(unconditionally)。
c、前戲後的協商 —— 一些優化
雖然說通過這種方式能夠減輕伺服器的壓力,解決一些請求資源時的效能問題。但是細細看來,還是存在一些浪費:每個都要去帶上條件請求伺服器來看資源是不是最新的,大多情況下是最新的情況下豈不是每次都在做無意義的驗證? 別急,做個約定就好,在 response
裡面加上 Cache-Control
和 Expires
即可。
通常他們是長這樣的:
cache-control:max-age=96247433
expires:Thu, 03 Jan 2019 04:24:16 GMT
Cache-control
用於控制HTTP快取(在HTTP/1.0中可能部分沒實現,僅僅實現了Pragma: no-cache)
;Expires
表示存在時間,允許客戶端在這個時間之前不去檢查(發請求),等同 max-age
的效果。但是如果同時存在,則被 Cache-Control
的 max-age
覆蓋。
題外話:Expires要求客戶端和服務端的時鐘嚴格同步。HTTP1.1引入Cache-Control來克服Expires頭的限制。如果max-age和Expires同時出現,則max-age有更高的優先順序。
d、前戲後的爭執
這麼多的欄位,現在數數好像已經有 6 個了,那麼他們是誰先誰後?
如果比較粗的說先後順序應該是這樣:
Cache-Control
—— 請求伺服器之前Expires
—— 請求伺服器之前If-None-Match (Etag)
—— 請求伺服器If-Modified-Since (Last-Modified)
—— 請求伺服器
需要注意的是 如果同時有 etag
和 last-modified
存在,在傳送請求的時候會一次性的傳送給伺服器,沒有優先順序,伺服器會比較這兩個資訊(在具體實現上,大多數做法針對這種情況只會比對 etag
)。伺服器在輸出上,如果輸出了 etag
就沒有必要再輸出 last-modified
(實際上大多數情況會都輸出)。
具體可以參見知乎上的這個問題——關於瀏覽器的快取,有了Etag
,last-Modified
還有必要存在嗎???。如果要深究,就要仔細看看 RFC
了,如果恰好你看了,歡迎在評論中提出。
e、完事後的討論 – 各種途徑的訪問
瀏覽器輸入 url 之後敲下回車,重新整理 F5 與強制重新整理(Ctrl + F5),又有什麼區別?
實際上瀏覽器輸入 url 之後敲下回車就是先看本地 cache-control、expires 的情況,重新整理(F5)就是忽略先看本地 cache-control、expires 的情況,帶上條件 If-None-Match、If-Modified-Since,強制重新整理(Ctrl + F5)就是不帶條件的訪問。
值得注意的是,如果是 瀏覽器輸入 url 之後敲下回車
你在 network
裡面看到的狀態往往是 200
,但是大小是 0。這是因為這個 200 是上次訪問資源返回的狀態碼。
如果你是一位開發者,還是建議在 Chrome
裡面開啟 Disable Cache
.