霖呆呆你來講說瀏覽器緩存吧

霖呆呆你來講說瀏覽器緩存吧

都知道瀏覽器緩存和HTTP知識對咱們前端的重要性, 可是一直都沒有對它進行很好的總結. 並且近幾天流感🦠鬧的挺嚴重的, 霖呆呆今年也是沒有去拜年了, 窩在家裏, 碼碼字吧.css

也是看了幾篇比較好的關於瀏覽器緩存的文章, 有所收穫, 因此想在這裏整理總結給你們, 就算之後面試問到了關於這方面的內容也能夠輕鬆應對😊.前端

不少時候, 面試官不會精確的問你某個知識點, 而是拋出一個有指向性可是又能夠很發散的問題.web

好比: 「嗯...那你說說瀏覽器緩存吧!」.面試

這時候考驗的就是你對瀏覽器知識的廣度與深度了.算法

當接到這樣一個問題的時候, 你得冷靜下來, 回想一下均可以從哪幾個方面說? 各個大的方面又能夠分爲幾個小的點說呢?瀏覽器

接下來, 讓我來爲你們拆分一下, 咱們能夠從如下幾個方面來回答這個問題:緩存

  • 緩存的類型 (強緩存or協商緩存)
  • 緩存位置 (Service Worker、Memory Cache...)
  • 緩存過程分析
  • 緩存策略的實際場景應用

緩存的類型

首先從緩存的類型上來講, 能夠分爲兩種: 強緩存協商緩存.安全

強緩存是不須要發送HTTP請求的, 而協商緩存須要.服務器

也就是在發送HTTP請求以前, 瀏覽器會先檢查一下強緩存, 若是命中直接使用,不然就進入下一步。網絡

強緩存

瀏覽器檢查強緩存的方式主要是判斷這兩個字段:

  • HTTP/1.0時期使用的是 Expires;
  • HTTP/1.1使用的是** Cache-Control**

(expires中文意思有效期, cache-control中文意思緩存管理)

Expires

Expires字面意思表示的是有效期, 那麼很好理解, 它表示的就是一個具體的時間.

例如🌰:

Expires: Wed, Nov 11 2020 08:00:00 GMT
複製代碼

表示的就是這個資源在2020年11月11日8點過時, 到了過時時間了就得向服務端發請求了.

頗有意思的是, 如果設置了Expires, 可是服務器的時間與瀏覽器的時間不一致的時候(好比你手動修改了本地的時間), 那麼就可能會形成緩存失效, 所以這種方式強緩存方式並非很準確, 它也所以在HTTP/1.1中被摒棄了.

Cache-Control

摒棄了Expires以後, HTTP/1.1採用了Cache-Control這個重要的規則. 它設置的是一個具體的過時時長, 其中的一個屬性是max-age.

例如🌰:

Cache-Control: max-age=300
複製代碼

表示的是這個資源在響應以後的300s內過時, 也就是5分鐘以內再次獲取這個資源會直接使用緩存.

Cache-Control不只僅有max-age 這一個屬性, 其實它有不少的用法, 你甚至能夠採用組合的方式:

Cache-Control: public, max-age=300
複製代碼

上面👆用法的意思是響應能夠被任何對象(客戶端, 代理服務器等)緩存, 且過時時長爲5分鐘.

(由於一個請求經歷的不只僅是客戶端(瀏覽器)和目標服務器, 它中間有可能會通過不一樣的代理服務器)

下面來例舉一些經常使用的指令:

public: 客戶端和代理服務器均可以緩存. 響應能夠被中間任何的一個節點緩存, 好比一個請求要經歷 Browser -> proxy1 -> proxy2 -> Server, 中間的代理(proxy)能夠緩存資源. 下次再請求同一資源的時候, 瀏覽器就會直接到proxy1中拿緩存的東西而沒必要向proxy2拿.

private: 這個是Cache-Control默認的取值, 只有客戶端能夠緩存, 中間節點不容許緩存. 在 Browser -> proxy1 -> proxy2 -> Server 這個過程當中, 代理(proxy)不會緩存任何數據, 當Browser再次請求時, proxy會把Server返回的數據發送給Brower, 作好請求轉發, 而不是給本身緩存的數據.

no-cache: 表示不進行強緩存驗證, 而是用協商緩存來驗證.

no-store: 全部內容都不會被緩存, 不進行強緩存, 也不進行協商緩存.

max-age: 表示在多久以後過時, 好比max-age=300表示在300s後緩存內容失效.

s-max-age: 它的做用和max-age很像, 不過max-age 用於普通緩存, 而s-max-age用於代理緩存, 且s-max-age的優先級更高.

max-stale: 能容忍的最大過時時間。max-stale指令表示客戶端願意接收一個已通過期了的響應。

min-fresh:可以容忍的最小新鮮度。min-fresh表示客戶端不肯意接受新鮮度很少於當前的age加上min-fresh設定的時間之和的響應。

基於上面👆的這些指令, 咱們能夠將它們進行組合, 達到多個目的, 不一樣的效果.

有一張來自《浪裏行舟-深刻理解瀏覽器的緩存機制》中的圖表述的很是好:

http1.png
http1.png

ExpiresCache-control的對比

  • Expires產於 HTTP/1.0, Cache-control產於 HTTP/1.1;
  • Expires設置的是一個具體的時間, Cache-control 能夠設置具體時常還有其它的屬性;
  • 二者同時存在, Cache-control的優先級更高;
  • 在不支持 HTTP/1.1的環境下, Expires就會發揮做用, 因此先階段的存在是爲了作一些兼容的處理.

協商緩存

在上面👆咱們已經介紹了強緩存, 它是不須要發送HTTP請求的, 如果強緩存失效, 則會進入協商緩存.

協商緩存歸納來講就是瀏覽器會攜帶緩存標識(tag)向服務器發送請求, 服務器會根據緩存標識(tag)來決定是否使用緩存.

因此對於服務器的返回結果會有這兩種狀況:

  • 協商緩存生效, 返回304和Not Modified(空的響應體)
  • 協商緩存失效, 返回200和請求結果

而剛剛提到的這個緩存標識(tag)也是有兩種.

分爲**Last-Modified** 和 ETag.

Last-Modified 和 If-Modified-Since

從字面意思上咱們能夠看出, Last-Modified表示的是資源的最後修改時間, 所以其中一種協商緩存判斷的就是最後修改時間.

那它具體是怎樣實現的呢🤔️?

其實使用Last-Modified進行協商緩存會通過如下幾步:

  1. 瀏覽器第一次向服務器請求這個資源
  2. 服務器在返回這個資源的時候, 在 response header中添加 Last-Modifiedheader, 值爲該資源在服務器上最後的修改時間
  3. 瀏覽器接收到後緩存文件和這個 header
  4. 當下次瀏覽器再次請求這個資源的時候, 檢測到有 Last-Modified這個 header, 就會在請求頭中添加 If-Modified-Since這個 header, 該值就是 Last-Modified
  5. 服務器再次接收到該資源的請求, 則根據 If-Modified-Since與服務器中的這個資源的最後修改時間作對比
  6. 對比結果相同則返回 304和一個空的響應體, 告訴瀏覽器從本身(瀏覽器)的緩存中拿
  7. 對比結果不一樣( If-Modified-Since < 服務器資源最後修改時間), 則表示資源被修改了, 則返回200和最新的資源文件(固然還包括最新的 Last-Modefied)
http2.png
http2.png

ETag 與 If-None-Match

ETag其實與Last-Modefied的原理差很少, 不過它不是根據資源的最後修改時間來判斷的, 而是經過一個惟一的標識😊.

在瀏覽器請求服務器資源的時候, 服務器會根據當前文件的內容, 給文件生成一個惟一的標識, 如果文件發生了改變, 則這個標識就會改變.

服務器會將這個標識ETag放到響應體的header中與請求的資源一塊兒返回給瀏覽器, 而瀏覽器一樣也會緩存文件與這個header.

在下一次再次加載該資源時, 瀏覽器會將剛剛緩存的ETag放到請求體頭部(request header)的If-None-Match裏發送給服務器.

一樣的服務器接收到了以後與該資源自身的ETag作對比, 若是一致, 則表示該資源未發生改變, 則直接返回304知會客戶端直接使用本地緩存便可. 如果不一致, 則返回200和最新的資源文件(固然還包括最新的ETag)

以下圖:

http3.png
http3.png

二者對比

在進行對比以前, 咱們先來看看二者都有什麼優缺點呢🤔️?

首先對於Last-Modified:

  • 如果本地打開了緩存文件, 並無進行修改, 也仍是會改變最後修改時間, 致使緩存失敗;
  • 因爲 Last-Modified是以秒來計時的, 如果某個文件在一秒內被修改了不少次, 那麼這時候的 Last-Modified 並無體現出修改了.

而後對於ETag:

  • 性能上的不足,只要文件發生改變, ETag就會發生改變. ETag須要服務器經過算法來計算出一個hash值.

總結, 因此對於兩種協商緩存:

  • 準確度上 ETag更強;
  • 性能上 Last-Modified更好;
  • 二者都支持的話, ETag優先級更高.

緩存位置

在上面👆咱們已經介紹完了緩存的類型😄, 可是以前也提到過了, 如果命中了強緩存或者服務器返回了304以後, 要瀏覽器從緩存中過去資源, 那這些緩存具體是存儲在哪裏呢?

從優先級上來講分爲如下四種:

  • Service Worker
  • Memory Cache
  • Disk Cache
  • Push Cache

Service Worker

Service Worker是運行在瀏覽器背後的獨立線程, 也就是說它脫離了瀏覽器的窗體, 沒法直接訪問DOM.功能上主要是能實現: 離線緩存消息推送網絡代理等.好比離線緩存就是Service Worker Cache.

簡單來講, 它有如下幾個特色:

  • 借鑑了 Web Worker是思路
  • 使用 Service Worker會涉及到請求攔截, 因此須要用 HTTPS協議來保證安全, 傳輸協議必須是 HTTPS
  • 與瀏覽器其它內建的緩存機制不一樣, 它可讓咱們自由的控制緩存哪些文件、如何匹配讀取緩存, 且緩存是持續性的
  • Service Worker同時也是 PWA的重要實現機制

Memory Cache

從命名上來講, Memory Cache就是內存中的緩存, 存儲的主要是當前頁面已經抓取到的資源, 好比頁面上已經下載的樣式腳本圖片等.

Memory Cache的特色:

  • 讀取效率快, 但是緩存持續時間短, 會隨着進程的釋放而釋放(一旦關閉 Tab頁面, 就被釋放了, 還有可能在沒關閉以前, 排在前面的緩存就失效了, 例如一個頁面的緩存佔用了超級多的內存)
  • 幾乎全部的請求資源都能進入 memory Cache, 細分來講主要分爲 preloaderpreload這兩塊.
  • 在從 memory Cache讀取緩存時, 瀏覽器會忽視 Cache-Control中的一些 max-age、no-cache等頭部配置, 除非設置了 no-store這個頭部配置.

preloader

上面👆提到的preloader是頁面優化的常見手段之一, 它的做用主要是用於在瀏覽器打開一個網頁的時候,可以一邊解析執行js/css, 一邊去請求下一個資源, 而這些被 preloader 請求來的的資源就會被放入 memory Cache 中,供以後的解析執行操做使用。

preload

preloadpreloader僅兩個字母之差, 它能顯式指定預加載的資源, 這些資源也會被放進memory Cache中, 例如<link rel="preload">

Disk Cache

Disk Cache, 也叫作HTTP Cache, 是存儲在硬盤上的緩存, 因此它是持久存儲, 是實際存在於文件系統中的.

從存儲效率上說, 它比內存緩存慢, 可是優點在於存儲容量更大, 且存儲時長更長.

在全部瀏覽器緩存中, Disk Cache是覆蓋面最大的. 它會根據前面咱們提到的HTTP header中的緩存字段來判斷哪些資源須要緩存, 哪些資源不須要請求而直接使用, 哪些已通過期了須要從新請求獲取.

如果命中了緩存以後, 瀏覽器會從硬盤中直接讀取資源, 雖然沒有從內存中讀取的快, 可是倒是比網絡緩存快.

前面提到的強緩存和協商緩存也是屬於Disk Cache, 它們最終都存儲在硬盤裏.

Memory CacheDisk Cache二者的對比:

  • 比較大的 JS、CSS文件會被丟硬盤中存儲, 反之則存儲在內存中
  • 當前系統內存使用率比較高的時候,文件優先進入磁盤

Push Cache

Push Cache(推送緩存), 它是瀏覽器緩存的最後一段防線, 當以上三種緩存都沒有命中的時候, 它纔會被使用.

我所知道的, 它只會在會話(Session)中存在, 一旦會話結束它就會被釋放, 而且緩存時間也很短暫, 在Chrome瀏覽器中只有5分鐘.

另外因爲它是 HTTP/2 中的內容, 所以在國內不是很普及, 這裏貼上一個比較好的總結:

  • 全部的資源都能被推送,而且可以被緩存,可是 Edge 和 Safari 瀏覽器支持相對比較差

  • 能夠推送 no-cache 和 no-store 的資源

  • 一旦鏈接被關閉,Push Cache 就被釋放

  • 多個頁面可使用同一個HTTP/2的鏈接,也就可使用同一個Push Cache。這主要仍是依賴瀏覽器的實現而定,出於對性能的考慮,有的瀏覽器會對相同域名但不一樣的tab標籤使用同一個HTTP鏈接。

  • Push Cache 中的緩存只能被使用一次

  • 瀏覽器能夠拒絕接受已經存在的資源推送

  • 你能夠給其餘域名推送資源

原文連接:https://www.jianshu.com/p/54cc04190252

緩存過程分析

上面👆已經向你們介紹了緩存類型已經緩存的位置, 那麼瀏覽器具體的一個緩存行徑是怎樣的呢?

從瀏覽器發起HTTP請求到得到請求結果, 能夠分爲如下幾個過程:

  1. 瀏覽器第一次發起 HTTP請求, 在瀏覽器緩存中沒有發現請求的緩存結果和緩存標識
  2. 所以向服務器發起 HTTP請求, 得到該請求的結果還有緩存規則(也就是 Last-Modified 或者 ETag)
  3. 瀏覽器把響應內容存入 Disk Cache, 把響應內容的引用存入 Memory Cache
  4. 把響應內容存入 Service WorkerCache Storage (若是 Service Worker 的腳本調用了 cache.put())

下一次請求相同資源的時候:

  1. 調用Service Workerfetch事件響應

  2. 查看memory Cache

  3. 查看disk Cache. 這裏細分爲:

    • 有強緩存且未失效, 則使用強緩存, 不請求服務器, 返回的狀態碼都是200
    • 有強緩存且已失效, 使用協商緩存判斷, 是返回304仍是200(讀取緩存仍是從新獲取)

緩存策略的實際場景應用

說了這麼多緩存策略, 那麼在實際使用上來講, 咱們通常是怎樣使用它的呢?

不常變化的資源

對於不常變化的資源:

Cache-Control: max-age=31536000
複製代碼

一般是給Cache-Control設置成一個很大的值, (31536000, 一年). 這個也很好理解, 不常變化的資源, 直接讓它使用緩存就是了.

可是有時候爲了解決更新的問題, 咱們須要在文件名中添加上hash, 版本號等動態字段, 這樣就達到了更改引用URL 的目的.

常變化的資源

常常變化的資源, 咱們進行如下配置:

Cache-Control: no-cache
複製代碼

設置成以上配置, 使得瀏覽器每次都請求服務器, 而後配合ETag或者Last-Modified來驗證資源是否有效.

後語

瀏覽器緩存的內容其實還有不少能夠說的, 霖呆呆這裏主要是總結了一些面試時常問到的, 你能夠轉化成本身的言語來回答面試官.

最近流感🦠的事情鬧的挺嚴重的呀, 小夥們出門必定要帶好口罩😷, 作好防禦措施...

參考文章:

《神三元-(1.6w字)瀏覽器與前端性能靈魂之問,請問你能接得住幾個?(上)》

《浪裏行舟-深刻理解瀏覽器的緩存機制》

小蘑菇哥哥-一文讀懂前端緩存

知識無價, 支持原創

相關文章
相關標籤/搜索