深入瀏覽器緩存

瀏覽器緩存詳解

寫這篇博客主要是想深入的談一談自己所理解的瀏覽器緩存,之前只看到瀏覽器的network中顯示 有from cache ,from disk.但是實際上不了解整個緩存機制。碰巧最近看到了幾篇非常好的文章,然後自己也打算記錄下。

四種瀏覽器端緩存

緩存可以說是性能優化中簡單高效的一種優化方式了。一個優秀的緩存策略可以縮短網頁請求資源的距離,減少延遲,並且由於緩存文件可以重複利用,還可以減少帶寬,降低網絡負荷。對於一個完成的請求過程來說,基本是發起請求-後端處理-瀏覽器響應渲染這三個階段,瀏覽器端可以進行優化,比如緩存機制。可以減少一些請求,比如請求的比較頻繁,數據未修改,就可以直接使用緩存中的數據,不需要重新請求服務器。
按照緩存存儲的位置來劃分的話瀏覽器端一共有四種緩存方式,分別爲service worker內存(memory cache)硬盤(disk cache)push cache

  1. service worker
    MDN是這樣介紹的:

Service workers
本質上充當Web應用程序與瀏覽器之間的代理服務器,也可以在網絡可用時作爲瀏覽器和網絡間的代理。它們旨在(除其他之外)使得能夠創建有效的離線體驗,攔截網絡請求並基於網絡是否可用以及更新的資源是否駐留在服務器上來採取適當的動作。他們還允許訪問推送通知和後臺同步API。

白話來說呢就是,Service Worker 是運行在瀏覽器後的獨立線程,一般可以用來實現緩存功能。使用 Service Worker的話,這裏需要注意的是傳輸協議必須爲 HTTPS。因爲 Service Worker 中涉及到請求攔截,所以必須使用 HTTPS 協議來保障安全。Service Worker 的緩存與瀏覽器其他內建的緩存機制不同,它可以讓我們自由控制緩存哪些文件、如何匹配緩存、如何讀取緩存,並且緩存是持續性的。
Service Worker 實現緩存功能一般分爲三個步驟:首先需要先註冊 Service Worker,然後監聽到 install 事件以後就可以緩存需要的文件,那麼在下次用戶訪問的時候就可以通過攔截請求的方式查詢是否存在緩存,存在緩存的話就可以直接讀取緩存文件,否則就去請求數據。 當 Service Worker 沒有命中緩存的時候,我們需要去調用 fetch 函數獲取數據。也就是說,如果我們沒有在 Service Worker 命中緩存的話,會根據緩存查找優先級去查找數據。但是不管我們是從 Memory Cache 中還是從網絡請求中獲取的數據,瀏覽器都會顯示我們是從 Service Worker 中獲取的內容。

  1. memory cache

    字表意思就是內存緩存,主要包含頁面中已經獲取到的資源,比如頁面的腳本文件、樣式文件、圖片等,內存的讀取肯定比磁盤快(磁盤需要進行I/O讀寫,所以比內存蠻)。內存中的緩存數據一般持續性很短,頁面關閉就會釋放。而且內存的大小一般容量都不是很大,相比較於磁盤來說是小很多了。os需要合理的分配每一塊可用的內存。電腦本身啓動的程序就會佔用大部分的內存,所以可以使用切用來作爲緩存數據的內存一般都很小了。
    在這裏插入圖片描述
    頁面刷新請求時可以查看network,一般from memory cache 的數據都是來源於內存中。可以發現刷新時很多數據都是來源於內存的。內存緩存存儲強緩存,即cache-control 和 Expires 。內存緩存在緩存資源時並不關心返回資源的HTTP緩存頭Cache-Control是什麼值,同時資源的匹配也並非僅僅是對URL做匹配,還可能會對Content-Type,CORS等其他特徵做校驗。

  2. 磁盤緩存
    顧名思義就是存在硬盤裏的數據,可以存儲的數據量比較大,但是相對來說讀取比較慢,比較內存緩存來說,硬盤緩存的有點主要體現在時效性上和容量上。硬盤緩存中存入的數據是根據http header中的字段判定的。哪些資源可以進行存儲,哪些不進行存儲。這裏又要提到兩個名詞了 etag 和 last-modified。也可以稱爲協商緩存。 一般協商緩存存儲在硬盤裏,至於具體的存儲條件,我們後續再聊。
    對於文件的存儲位置如何選擇呢,大概可以這樣記錄。
    1>.大文件基本存儲到硬盤緩存(至少是非內存緩存)。
    2>.經常需要訪問的存儲到硬盤。

  3. push cache
    這個也是我今天剛瞭解的新名詞,譯名被叫爲”推送緩存“ ,是http/2中的內容,當上邊的三種緩存都沒有生效的時候,便可以使用推送緩存,這個有一定的侷限性,他只在session中存在,一旦會話失效,此緩存也會跟着失效,時效性也很短。
    這裏就瞭解到幾點結論:
    1>. push cache 只能使用一次。
    2>.所有資源都會被推送,而瀏覽器可以拒絕接受已存在的資源的推送。
    3>.一旦連接被關閉,Push Cache 就被釋放.
    4>.多個頁面可以使用同一個HTTP/2的連接,也就可以使用同一個Push Cache。這主要還是依賴瀏覽器的實現而定,出於對性能的考慮,有的瀏覽器會對相同域名但不同的tab標籤使用同一個HTTP連接。
    如果以上四種緩存都沒有命中,只能去重新發起請求來獲取資源了。

緩存的整個過程

瀏覽器和服務器通過請求應答的方式,發起請求-服務器響應請求-獲取資源。那麼,瀏覽器怎麼確定當前的資源應不應該緩存呢?上邊我們說過,是根據服務器端響應的標識來確定的,瀏覽器第一次向服務器發起該請求後拿到請求結果後,將請求結果和緩存標識存入瀏覽器緩存,瀏覽器對於緩存的處理是根據第一次請求資源時返回的響應頭來確定的。
基本過程如下:
在這裏插入圖片描述
瀏覽器每次發出請求的時候,都會先在瀏覽器緩存中查找該請求的結果以及緩存標識,而瀏覽器每次也會根據服務器響應的標識來決定是否對數據進行緩存,緩存到哪裏。
根據是否需要向服務器重新發起HTTP請求將緩存過程分爲兩個部分,分別是強緩存和協商緩存。

強緩存

強緩存不會向服務器發送請求,直接從緩存中讀取資源,在chrome控制檯的Network選項中可以看到該請求返回200的狀態碼,並且Size顯示from disk cache或from memory cache。強緩存可以通過設置兩種 HTTP Header 實現:Expires 和 Cache-Control。

1.Expires
緩存過期時間,用來指定資源到期的時間,是服務器端的具體的時間點。也就是說,Expires=max-age + 請求時間,需要和Last-modified結合使用。Expires是Web服務器響應消息頭字段,在響應http請求時告訴瀏覽器在過期時間前瀏覽器可以直接從瀏覽器緩存取數據,而無需再次請求。
Expires 是 HTTP/1 的產物,受限於本地時間,如果修改了本地時間,可能會造成緩存失效。 Expires:Wed,22Oct201808:41:00GMT表示資源會在 Wed, 22 Oct 2018 08:41:00 GMT 後過期,需要再次請求。

2.Cache-Control
在HTTP/1.1中,Cache-Control是最重要的規則,主要用於控制網頁緩存。比如當 Cache-Control:max-age=300時,則代表在這個請求正確返回時間(瀏覽器也會記錄下來)的5分鐘內再次加載資源,就會命中強緩存。
cache-control可以有多個值:每個值得意義我在這裏解釋一下。
public:所有內容都將被緩存(客戶端和代理服務器都可緩存)。具體來說響應可被任何中間節點緩存,如 Browser <-- proxy1 <-- proxy2 <-- Server,中間的proxy可以緩存資源,比如下次再請求同一資源proxy1直接把自己緩存的東西給 Browser 而不再向proxy2要。不需要到server去取數據。

private:所有內容只有客戶端可以緩存,Cache-Control的默認取值。具體來說,表示中間節點不允許緩存,對於Browser <-- proxy1 <-- proxy2 <-- Server,proxy 會老老實實把Server 返回的數據發送給proxy1,自己不緩存任何數據。當下次Browser再次請求時proxy會做好請求轉發而不是自作主張給自己緩存的數據。

no-cache:客戶端緩存內容,是否使用緩存則需要經過協商緩存來驗證決定。表示不使用 Cache-Control的緩存控制方式做前置驗證,而是使用 Etag 或者Last-Modified字段來控制緩存。需要注意的是,no-cache這個名字有一點誤導。設置了no-cache之後,並不是說瀏覽器就不再緩存數據,只是瀏覽器在使用緩存數據時,需要先確認一下數據是否還跟服務器保持一致。

no-store:所有內容都不會被緩存,即不使用強制緩存,也不使用協商緩存,直接請求服務器。

max-age:max-age=xxx (xxx is numeric)表示緩存內容將在xxx秒後失效

s-maxage(單位爲s):同max-age作用一樣,只在代理服務器中生效(比如CDN緩存)。比如當s-maxage=60時,在這60秒中,即使更新了CDN的內容,瀏覽器也不會進行請求。max-age用於普通緩存,而s-maxage用於代理緩存。s-maxage的優先級高於max-age。如果存在s-maxage,則會覆蓋掉max-age和Expires header。

max-stale:能容忍的最大過期時間。max-stale指令標示了客戶端願意接收一個已經過期了的響應。如果指定了max-stale的值,則最大容忍時間爲對應的秒數。如果沒有指定,那麼說明瀏覽器願意接收任何age的響應(age表示響應由源站生成或確認的時間與當前時間的差值)。

min-fresh:能夠容忍的最小新鮮度。min-fresh標示了客戶端不願意接受新鮮度不多於當前的age加上min-fresh設定的時間之和的響應。

3.Expires和Cache-Control兩者對比

區別就在於 Expires 是http1.0的產物,Cache-Control是http1.1的產物,兩者同時存在的話,Cache-Control優先級高於Expires;Expires主要是用來兼容HTTP1.0。已經過時且存在弊端。expires時間根據本地時間而來,如果改變本地的時間。有可能會使當前的expires緩存失效。 強緩存判斷是否緩存的依據來自於是否超出某個時間或者某個時間段,而不關心服務器端文件是否已經更新,這可能會導致加載文件不是服務器端最新的內容,那我們如何獲知服務器端內容是否已經發生了更新呢?此時我們需要用到協商緩存策略。

對比緩存又叫協商緩存

協商緩存就是強制緩存失效後,瀏覽器攜帶緩存標識向服務器發起請求,由服務器根據緩存標識決定是否使用緩存的過程,主要有以下兩種情況:

在這裏插入圖片描述
協商緩存生效,返回304和Not Modified。
在這裏插入圖片描述
協商緩存失效,返回200和請求結果
如圖所示,http請求攜帶的緩存標識可以有兩個,分別是 Last-modified和Etag,接下來我們慢慢說一說這兩個。
Last-modified和if-Modified-since
直譯過來就是最後的修改時間,根據比對修改時間可以確定在這一段時間裏資源是否進行了修改。最小顆粒爲S,這也就說明了這個屬性的弊端,如果在一秒以內修改多次,則數據不會更新。瀏覽器第一次請求的時候,響應資源的響應頭中添加 last-modified,數值爲資源在服務器的最後修改時間。瀏覽器下一次請求的時候,檢測到瀏覽器中有這個header,會添加if-modified-since這個header,值爲資源最後修改時間的值即last-modified得值。服務器再次收到這個資源請求,會根據 If-Modified-Since 中的值與服務器中這個資源的最後修改時間對比,如果兩個值相等,返回304和空的響應體,直接從緩存讀取,如果If-Modified-Since的時間小於服務器中這個資源的最後修改時間,說明文件有更新,於是返回新的資源文件和200。
弊端:
1.如果本地打開緩存文件,即使沒有對文件進行修改,但還是會造成 Last-Modified 被修改,服務端不能命中緩存導致發送相同的資源
2.因爲 Last-Modified 只能以秒計時,如果在不可感知的時間內修改完成文件,那麼服務端會認爲資源還是命中了,不會返回正確的資源
last-modified是http1.0得產物,由於這兩個弊端產生了Etag和if-none-macth
Etag

Etag是服務器響應請求時,返回當前資源文件的一個唯一標識,一般是一個hash值,只要資源有變化,Etag就會重新生成。瀏覽器在下一次加載資源向服務器發送請求時,會將上一次返回的Etag值放到request header裏的If-None-Match裏,服務器只需要比較客戶端傳來的If-None-Match跟自己服務器上該資源的ETag是否一致,就能很好地判斷資源相對客戶端而言是否被修改過了。如果服務器發現ETag匹配不上,那麼直接以常規GET 200回包形式將新的資源(當然也包括了新的ETag)發給客戶端;如果ETag是一致的,則直接返回304知會客戶端直接使用本地緩存即可。這裏就避免了last-modified得秒級問題。只要數據變化就產生新的etag值。

精度上來看,Etag是優於last-modified的。至少精度準確。性能上來說etag要產生hash值,last-modified只需要記錄當前修改時間就可以了,這裏來說last-modified的性能是優於Etag的。

緩存機制

我們介紹了這麼多,但是具體的使用的先後順序是什麼呢?首先呢 強制緩存優先於協商緩存進行,若強制緩存(Expires和Cache-Control)生效則直接使用緩存,若不生效則進行協商緩存(Last-Modified / If-Modified-Since和Etag / If-None-Match),協商緩存由服務器決定是否使用緩存,若協商緩存失效,那麼代表該請求的緩存失效,返回200,重新返回資源和緩存標識,再存入瀏覽器緩存中;生效則返回304,繼續使用緩存。

實際場景應用緩存策略

1.對於頻繁變動的資源最好不要使用no-cache,Cache-Control:no-cache 使瀏覽器每次都請求服務器,然後配合 ETag 或者 Last-Modified 來驗證資源是否有效。這樣的做法雖然不能節省請求數量,但是能顯著減少響應數據大小。
2. 不常變動的資源則可以使用 cache-control:max-age=長時間 (這裏可以根據項目和資源而定)

頁面刷新的幾種方式

  1. 輸入地址欄 回車,由於是新開tab,memory cache頁面關閉則不可用。查找disk cache是否可用,如果可用則使用。
  2. F5 頁面沒有關閉,內存緩存有效,會出現memory cache的情況。隨後使用 disk cache。
  3. ctrl+F5 全部去服務器請求信的資源,不適用任何緩存文件。

如果覺着不錯,可以關注我的公衆號
在這裏插入圖片描述

參考文章,翻譯文章

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章