強制快取的思想是,在瀏覽器內建資料庫中快取每次請求中 「可以被快取」 (受到一些關鍵字的管控)的靜態資源如 image, css, js 檔案, 當第二次請求被快取過的資源時候,會通過校驗兩個欄位 Expires 和 Cache-Control 的max-age欄位(注意,Expires 是 http1.0 的產物, Cache-Control 則是 http1.1 的產物。 兩者同時存在, 或者只存在其中之一, 都可以觸發強制快取
當滿足欄位約束的情況下, 瀏覽器就不會向伺服器傳送請求而是直接從伺服器返回資料, 同時其狀態碼為 200
當不滿足欄位約束的情況下, 瀏覽器則會向伺服器正常傳送請求
強制快取主要取決於兩個欄位 Expires 和 Cache-Control 中的 max-age 欄位, 在兩個響應頭都存在的情況下, 其流程如圖
當兩個欄位同時存在得到時候, Cache-Control 中的 max-age 欄位欄位優先順序會稍微高一點, 當 Cache-Control 中的 max-age 欄位校驗成功,會直接返回瀏覽器內建資料庫的快取, 失效時才會將決策權傳遞給 Expires 欄位判斷。
這樣設計的原因,大概是因為 Expires 欄位在設計時存在了這麼一個缺陷——Expires欄位返回的是伺服器的時間, 而非使用者端的本機時間。 當存在時差, 或者客戶修改本地時間的情況下 Expires 欄位會存在失效的可能性,比如 當同一時刻下的伺服器時間為 2022/4/26 06:00:00 使用者端時間為 2022/4/26 12:00:00 過期時間為兩個小時之後, 則伺服器會返回 2022/4/26 08:00:00 這個時間對應的值。由於瀏覽器執行在客戶環境下,對於客戶而言, 這個快取已經過期了,雖然快取確實有效, 但是對於瀏覽器而言這個快取確確實實是 「過期了」, 這會導致強制快取永遠不會生效!
那麼為了解決Expires 欄位這個問題, http 1.1 協定中新增了 Cache-Control 中的 max-age, 他是一個相對值, 即使用者端獲取到這個檔案多少秒後失效, 其判別權力全權交由瀏覽器, 這會相對更準確些。
協商快取主要由 ETag 和 Last-Modified 兩個欄位來實現
(通過上述兩個欄位就可以判斷當前檔案是否是最新的資料)
與上述兩個欄位配對的分別是 If-None-Match 和 If-Modified-Since 這兩個欄位,
瀏覽器首次向伺服器請求資料 A, 伺服器正常返回資料,同時在響應頭中放入 ETag 和 Last-Modified 兩個新欄位。
當瀏覽器第二次向伺服器請求資料 A 時, 瀏覽器會自動地在請求頭附上 If-None-Match 和 If-Modified-Since 兩個欄位(分別對應的是 ETag 和 Last-Modified 的值,兩兩相等), 然後由伺服器端進行校驗, 校驗通過的話(表明資料有效), 伺服器會直接返回 狀態碼 304 ,且不攜帶響應體的報文段, 這相當於告訴瀏覽器:當前快取有效, 可以直接使用! 校驗失敗則會和首次請求一樣, 返回狀態碼為200且攜帶資料響應體的報文段, 同時這個響應頭會帶上新的ETag 和Last-Modified, 為下一次協商快取做好鋪墊 。
需要注意的是, 在不用框架的情況下, 協商快取需要由後端開發人員手動實現,因此 ETag 和 Last-Modified 兩個欄位的優先順序取決於開發者, 但是 Last-Modified 這個欄位可以記錄的時間戳精確度是有一定限制的,如果連續多次資料更新在精確度範圍外, 會產生精確度丟失, 因此通常會讓ETag 的優先順序高於 Last-Modified 欄位(類似於Cache-control中max-age一樣, 屬於是後續改進協定的一個新欄位, 因此優先順序一般會高點)
預設情況下, 瀏覽器會優先考量強制快取的情況, 當強制快取生效的情況下, 請求並不會到達伺服器, 因此也就不會觸發協商快取。 當強制快取失效的時候, 瀏覽器便會將請求傳遞到伺服器, 於是伺服器又會開始校驗 If-Modified-Since 和 If-None-math 兩個欄位, 重複上述協商快取的一個執行流程
乍一看,兩者並存的情況, 有點像是兩個協定的簡單疊加,此時的協商快取更像是強制快取的兜底策略, 很可能協商快取很長一段時間都不會生效(強制快取過期時間設定過長的情況下), 因為強制快取的優先順序是要高於協商快取的。 當然這並不是我們想看到的, 比方說當後端資料確實變更了, 而此時的瀏覽器由於使用了強制快取,則會出現資料不一致的情況, 因此在這裡引入了請求頭中的兩個欄位 no-cache, 當使用了 no-cache 欄位的時候, 瀏覽器將不再使用強制快取, 而是直接去請求伺服器, 這個時候就會用到協商快取了(順帶一提的是, 還有一個 no-store 欄位, 用了這個欄位瀏覽器則不會在使用快取的資料也不快取資料,即強制快取和協商快取都失效了)