前言
掌握緩存策略,對提高前端頁面的性能有非常大的幫助,同時也能減少服務端的壓力。
同時,這也是前端面試中常問的問題,不管怎麼樣,這系列知識點在實戰應用中很重要,需要理解透徹。
下面講下,緩存中的強緩存和協商緩存。(備註,服務端均由 Express 提供服務)
強緩存
Expires
服務端給客戶端設置的 Expires 過期時間響應頭,客戶端下次再次請求時,會通過本地時間和該時間做對比。
如果本地時間大於 Expires 設置的時間,將重新向服務端獲取資源。
下圖,服務器設置了1小時的緩存,當客戶端再次請求資源時,http 狀態碼為 200,但能看到 from memory cache 的提示(證明資源文件是從本地拿的):
代碼示意:
express.static 是由 serve-static 提供基礎功能的,幫助我們快速設置緩存相關選項。
這裡著重看下紅框內容,如何設置 Expires 響應頭:
Expires 似乎解決了緩存問題,但光光靠它遠遠不夠,最起碼我們現在看到他有這些不足:
- 服務端需要設置一個固定的 Date 時間
- 強依賴客戶端時間,我們知道本地時間是可以修改的,會導致緩存失效等問題
題外話:
這裡注意到 UTC 時間格式,主要為了區別 GMT,如果沒有設置的話,將是這樣(多 8 個小時):
另外,關於時區 timezone 問題,可以參考 sequelize 和 moment-timezone 之間的處理(題外話,可能部分同學會遇到類似問題,這裡帶到下):
Cache-Control
當我們開啟 express static 的 cacheControl 默認配置,能看到 Expires 失效了。
默認 max-age 為 0 ,但這已經證明一點:Cache-Control 的優先級高於 Expires。
我們可以對 max-age 專門設置一個增量的緩存時間:
這樣客戶端每次請求在這個時間範圍內,就屬於緩存,也就不會向服務器請求資源了。
緩存參數
Cache-Control 還涉及 no-cache/no-store 和 public/private 等具體參數:
- public:整個網絡請求中的任何中間層都可以緩存資源
- private:對比 public,只有客戶端可以緩存資源
- no-cache:交給服務端判斷是否要緩存,跳過強緩存,走入下一環節:協商緩存
- no-store:不緩存,每次都從服務器獲取資源
協商緩存
200 和 304
首先要搞清一個問題,什麼時候瀏覽器請求資源的 http 狀態碼會返回 200 或者 304。
上述的強緩存都會查看本地環境,驗證緩存狀態,如果沒過期,也會響應 200,只是要注意後面的備註信息(from disk、from memory)。
相反,如果設置了 no-cache 等協商緩存,本地緩存就會忽略,向服務器驗證資源是否有更新,沒更新則返回 304。
Last-Modified
Last-Modified 是服務器端設置的響應頭,返回請求資源對應的服務器資源文件的修改時間戳。用於驗證資源文件是否被修改過。精度比 ETag 低。
示例
注意,express 中:默認 Last-Modified 和 Etag 是打開的。
首次請求時,如果服務器設置了 Last-Modified 請求頭,則會把請求資源文件的最後次修改時間戳返回:
當我嘗試服務器端修改該文件後,再次請求改文件,時間戳為最新一次,可以對比前後時間的不同:
當本地沒有緩存時,會向服務器請求資源,同時會增加額外的 If-Modified-Since 請求頭,以讓服務器做判斷。
如果服務器資源沒有更新過,則會響應 304 狀態碼,並不會發送資源文件(能看到沒有 Content-Length):
反之,如果服務器資源文件更新過,則請求頭中的
If-Modified-Since 和服務器資源文件的時間戳將會不一致,服務器判斷後會響應資源。同時服務器將響應 200 狀態碼,且發送資源文件:缺點
服務器資源文件管理問題,導致服務器緩存失效:
因為服務器資源文件只要更新/修改過,其文件時間都會更新。如果因為發佈,或者其他原因導致這樣的變化,則會讓服務器響應沒必要的更新。(因為文件內容沒有變化過)
Etag
彌補 Last-Modified 的一些不足之處,為服務器資源文件生成一個"指紋"值,用於精確對比文件更新狀態。
首次請求後,服務器會給客戶端一個 Etag 值,下次請求時將以 If-None-Match 向服務器傳送客戶端 tag 值,如果服務器判斷一致,則相應 200,否則 304。
上例中,已經的圖例中已經涉及了 Etag 場景,注意對比兩個 Etag "指紋"值:
幾種刷新操作對比
總結
簡單說了幾種緩存相關的概念,具體運用還是要具體分析,配合前端工程化一系列的構建輸出,友好的和緩存機制共存是個時刻要磨合的。(大廠都有一套"完美"的最佳實踐,但對於我來說還需要積累經驗)
閱讀更多 前端雨爸 的文章