瀏覽器- 瀏覽器緩存

總結一下在網上看到的一些瀏覽器緩存的知識點

一、緩存概述

影響一個網站性能的最直觀體驗就是網頁的打開速度,而提高網頁速度的方法之一就是使用緩存。一個優秀的緩存策略可以縮短網頁請求資源的距離,減少延遲,並且由於緩存文件可以重複利用,還可以減少帶寬,降低網絡負荷。

比較常見的就是瀏覽器會緩存訪問過網站的網頁,當再次訪問這個URL地址的時候,如果網頁沒有更新,就不會再次下載網頁,而是直接使用本地緩存的網頁。只有當網站明確標識資源已經更新,瀏覽器纔會再次下載網頁。

對於一個數據請求來說,可以分爲發起網絡請求、後端處理、瀏覽器響應三個步驟。瀏覽器緩存可以幫助我們在第一和第三步驟中優化性能。比如說直接使用緩存而不發起請求,或者發起了請求但後端存儲的數據和前端一致,那麼就沒有必要再將數據回傳回來,這樣就減少了響應數據。

1. web緩存

web緩存是指一個web資源(如html頁面,圖片,js,數據等)存在於web服務器和客戶端(瀏覽器)之間的副本。

web緩存的作用

  1. 減少網絡帶寬消耗
  2. 降低服務器壓力(可以重複使用本地的緩存,減少對服務器的請求)
  3. 減少網絡延遲,加快頁面打開速度

web緩存的類型

web緩存大致可以分爲以下幾種類型:

  1. 數據庫數據緩存
  2. 服務器端緩存
  3. 瀏覽器端緩存
  4. web應用層緩存

前端緩存主要是分爲HTTP緩存和瀏覽器緩存。其中HTTP緩存是在HTTP請求傳輸時用到的緩存,主要在服務器代碼上設置;而瀏覽器緩存則主要由前端開發在前端js上進行設置。

2. HTTP緩存

概述

HTTP緩存都是從第二次請求開始的。第一次請求資源時,服務器返回資源,並在respone header頭中回傳資源的緩存參數;第二次請求時,瀏覽器判斷這些請求參數,擊中強緩存就直接200,否則就把請求參數加到request header頭中傳給服務器,看是否擊中協商緩存,擊中則返回304,否則服務器會返回新的資源。

緩存的位置

從緩存位置上來說分爲四種,並且各自有優先級:

  1. Service Worker
  2. Memory Cache
  3. Disk Cache
  4. Push Cache

緩存策略

根據是否需要向服務器重新發起HTTP請求將緩存過程分爲兩個部分:

  1. 強緩存
  2. 協議緩存

status code 的區別

  • 200 memory : 不訪問服務器,關閉瀏覽器後資源被釋放 ,強緩存
  • 200 disk:不訪問服務器,關閉瀏覽器後資源依然存在,強緩存
  • 304 not modify: 訪問服務器,發現數據沒有更新,服務器返回此狀態碼。然後從緩存中讀取數據。協商緩存200 : 返回資源 size 從服務器端下
  • 載最新資源

3. 瀏覽器緩存

瀏覽器緩存即本地存儲,主要有以下幾種,localStorage,sessionStorage,cookie,和IndexDB

現在緩存的分類有點混亂,有人會分 HTTP緩存和瀏覽器緩存,也有人分的是瀏覽器緩存和本地緩存,下文以 瀏覽器緩存 指代 HTTP緩存
所以,下文主要是 HTTP 緩存

二、緩存的位置

從緩存位置上來說分爲四種,並且各自有優先級,當依次查找緩存且都沒有命中的時候,纔會去請求網絡。

  1. Service Worker
    Service Worker 是運行在瀏覽器背後的獨立線程,一般可以用來實現緩存功能。使用 Service Worker的話,傳輸協議必須爲 HTTPS。因爲 Service Worker 中涉及到請求攔截,所以必須使用 HTTPS 協議來保障安全。Service Worker 的緩存與瀏覽器其他內建的緩存機制不同,它可以讓我們自由控制緩存哪些文件、如何匹配緩存、如何讀取緩存,並且緩存是持續性的。

    當 Service Worker 沒有命中緩存的時候,我們需要去調用 fetch 函數獲取數據。也就是說,如果我們沒有在 Service Worker 命中緩存的話,會根據緩存查找優先級去查找數據。但是不管我們是從 Memory Cache 中還是從網絡請求中獲取的數據,瀏覽器都會顯示我們是從 Service Worker 中獲取的內容。

  2. Memory Cache
    Memory Cache 也就是內存中的緩存,主要包含的是當前中頁面中已經抓取到的資源,例如頁面上已經下載的樣式、腳本、圖片等。讀取內存中的數據肯定比磁盤快,內存緩存雖然讀取高效,可是緩存持續性很短,會隨着進程的釋放而釋放。 一旦我們關閉 Tab 頁面,內存中的緩存也就被釋放了。

    那麼既然內存緩存這麼高效,我們是不是能讓數據都存放在內存中呢?
    這是不可能的。計算機中的內存一定比硬盤容量小得多,操作系統需要精打細算內存的使用,所以能讓我們使用的內存必然不多。

    需要注意的事情是,內存緩存在緩存資源時並不關心返回資源的HTTP緩存頭Cache-Control是什麼值,同時資源的匹配也並非僅僅是對URL做匹配,還可能會對Content-Type,CORS等其他特徵做校驗。
    在這裏插入圖片描述

  3. Disk Cache
    Disk Cache 也就是存儲在硬盤中的緩存,讀取速度慢點,但是什麼都能存儲到磁盤中,比之 Memory Cache 勝在容量和存儲時效性上。

    在所有瀏覽器緩存中,Disk Cache 覆蓋面基本是最大的。它會根據 HTTP Herder 中的字段判斷哪些資源需要緩存,哪些資源可以不請求直接使用,哪些資源已經過期需要重新請求。並且即使在跨站點的情況下,相同地址的資源一旦被硬盤緩存下來,就不會再次去請求數據。絕大部分的緩存都來自 Disk Cache
    以下圖片的請求,在另開一個tab頁,因爲之前已經加載過圖片資源,所以直接從 disk cache 中取緩存
    在這裏插入圖片描述

  4. Push Cache
    Push Cache(推送緩存)是 HTTP/2 中的內容,當以上三種緩存都沒有命中時,它纔會被使用。它只在會話(Session)中存在,一旦會話結束就被釋放,並且緩存時間也很短暫,在Chrome瀏覽器中只有5分鐘左右,同時它也並非嚴格執行HTTP頭中的緩存指令。

    Push Cache 在國內能夠查到的資料很少,也是因爲 HTTP/2 在國內不夠普及。

如果以上四種緩存都沒有命中的話,那麼只能發起請求來獲取資源了。

那麼爲了性能上的考慮,大部分的接口都應該選擇好緩存策略,通常瀏覽器緩存策略分爲兩種:強緩存和協商緩存,並且緩存策略都是通過設置 HTTP Header 來實現的。爲了方便大家理解,也可以根據是否需要向服務器重新發起HTTP請求將緩存過程分爲兩個部分。

三、緩存過程分析

在這裏插入圖片描述

由上圖我們可以知道:

  1. 瀏覽器每次發起請求,都會先在瀏覽器緩存中查找該請求的結果以及緩存標識

  2. 瀏覽器每次拿到返回的請求結果都會將該結果和緩存標識存入瀏覽器緩存中

以上兩點結論就是瀏覽器緩存機制的關鍵,它確保了每個請求的緩存存入與讀取,只要我們再理解瀏覽器緩存的使用規則,那麼所有的問題就迎刃而解了。

對於瀏覽器端的緩存來講,緩存規則是在HTTP協議頭和HTML頁面的 Meta標籤中定義的。他們分別從新鮮度和校驗值兩個維度來規定瀏覽器是直接使用緩存中的副本,還是需要去源服務器獲取更新的版本。

  1. 新鮮度(過期機制):也就是緩存副本有效期。一個緩存副本必須滿足以下任一條件,瀏覽器會認爲它是有效的,足夠新的,而直接從緩存中獲取副本並渲染:

    1. 含有完整的過期時間控制頭信息(HTTP協議報頭),並且仍在有效期內
    2. 瀏覽器已經使用過這個緩存副本,並且在一個會話中已經檢查過新鮮度
  2. 校驗值(驗證機制):服務器返回資源的時候有時在控制頭信息帶上這個資源的實體標籤Etag(Entity Tag),它可以用來作爲瀏覽器再次請求過程的校驗標識。如過發現校驗標識不匹配,說明資源已經被修改或過期,瀏覽器需求重新獲取資源內容。

一個用戶發起一個靜態資源請求的時候,瀏覽器會通過以下幾步來獲取並展示資源:
在這裏插入圖片描述
緩存行爲主要由緩存策略決定,而緩存策略由內容擁有者設置。這些策略主要通過特定的HTTP頭部來清晰地表達。

以上過程也可以被概括爲三個階段:

  1. 本地緩存階段:先在本地查找該資源,如果有發現該資源,而且該資源還沒有過期,就使用這一個資源,完全不會發送http請求到服務器;

  2. 協商緩存階段:如果在本地緩存找到對應的資源,但是不知道該資源是否過期或者已經過期,則發一個http請求到服務器,然後服務器判斷這個請求,如果請求的資源在服務器上沒有改動過,則返回304,讓瀏覽器使用本地找到的那個資源;

  3. 緩存失敗階段:當服務器發現請求的資源已經修改過,或者這是一個新的請求(在本來沒有找到資源),服務器則返回該資源的數據,並且返回200, 當然這個是指找到資源的情況下,如果服務器上沒有這個資源,則返回404。

四、強緩存

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

瀏覽器緩存的控制

  1. 使用HTML的 Meta 標籤
    < META HTTP-EQUIV="Pragma" CONTENT="no-cache">
    
    上述代碼的作用是告訴瀏覽器當前頁面不被緩存,每次訪問都需要去服務器拉取。使用上很簡單,但只有部分瀏覽器可以支持,而且所有緩存代理服務器都不支持,因爲代理不解析HTML內容本身。
  2. 使用緩存有關的HTTP消息報頭
    一個URI的完整HTTP協議交互過程是由HTTP請求和HTTP響應組成的。
    在HTTP請求和響應的消息報頭中,常見的與緩存有關的消息報頭有:
    在這裏插入圖片描述
    接下來詳細講一下

Cache-Control

  1. max-age(單位爲s)指定設置緩存最大的有效時間,定義的是時間長短。當瀏覽器向服務器發送請求後,在max-age這段時間裏瀏覽器就不會再向服務器發送請求了。我們來找個資源看下。比如QQ推廣上的css資源,max-age=3600,也就是說緩存有效期爲3600秒(也就是1h)。於是在1小時內都會使用這個版本的資源,即使服務器上的資源發生了變化,瀏覽器也不會得到通知。(只接受 Age 值小於 max-age 值,並且沒有過期的對象)
    在這裏插入圖片描述

  2. max-stale:(可以接受過去的對象,但是過期時間必須小於 max-stale 值)

  3. min-fresh:(接受其新鮮生命期大於其當前 Age 跟 min-fresh 值之和的緩存對象)

  4. s-maxage(單位爲s)同max-age,只用於共享緩存(比如CDN緩存)。比如,當s-maxage=60時,在這60秒中,即使更新了CDN的內容,瀏覽器也不會進行請求。也就是說max-age用於普通緩存,而s-maxage用於代理緩存。如果存在s-maxage,則會覆蓋掉max-age和Expires header。

  5. public 指定響應會被緩存,並且在多用戶間共享。也就是下圖的意思。如果沒有指定public還是private,則默認爲public。(可以用 Cached 內容迴應任何用戶)
    在這裏插入圖片描述

  6. private 響應只作爲私有的緩存(見下圖),不能在用戶間共享。如果要求HTTP認證,響應會自動設置爲 private。(只能用緩存內容迴應先前請求該內容的那個用戶)
    在這裏插入圖片描述

  7. no-cache 指定不緩存響應,表明資源不進行緩存,但是設置了 no-cache 之後並不代表瀏覽器不緩存,而是在獲取緩存前要向服務器確認資源是否被更改。因此有的時候只設置 no-cache 防止緩存還是不夠保險,還可以加上 private 指令,將過期時間設爲過去的時間。(可以緩存,但是隻有在跟WEB服務器驗證了其有效後,才能返回給客戶端)

  8. no-store 不允許緩存,每次請求資源都要從服務器重新獲取。

  9. must-revalidate 指定如果頁面是過期的,則去服務器進行獲取。這個指令並不常用,就不做過多的討論了。

  10. cache-control種類的使用方法
    在這裏插入圖片描述

  11. 碎碎念,一開始在接口都沒看到Cache-Control 這個屬性,就很疑惑,查了下資料發現,這個屬性要後端有做相應設置纔會出現。發一下後端設置這個屬性的代碼

    Apache
    .htaccess文件
    ```
    <filesMatch "\.(ico|gif|jpg|png|jpeg)$"> 
    ExpiresActive On 
    ExpiresDefault "access plus 11 month" 
    Header append Cache-Control "public" 
    </filesMatch>
    ```
    
    Nginx
    .conf文件
    ```
    location ~* ^.+\.(jpg|jpeg|gif|png|ico)$
    { 
    expires max; 
    }
    ```
    

Expires

緩存過期時間,用來指定資源到期的時間,是服務器端的具體的時間點。也就是說, Expires=max-age + 請求時間 ,需要和Last-modified結合使用。但在上面我們提到過,cache-control的優先級更高。Expires是Web服務器響應消息頭字段,在響應http請求時告訴瀏覽器在過期時間前瀏覽器可以直接從瀏覽器緩存取數據,而無需再次請求。
在這裏插入圖片描述

五、協議緩存

協議緩存就是強制緩存失效後,瀏覽器攜帶緩存標識向服務器發起請求,由服務器根據緩存標識決定是否使用緩存的過程,主要有以下兩種情況:
1. 協商緩存生效,返回304和Not Modified
2. 協商緩存失效,返回200和請求結果

Last-modified & If-modified-since

服務器端文件的最後修改時間,需要和cache-control共同使用,是檢查服務器端資源是否更新的一種方式。當瀏覽器再次進行請求時,會向服務器傳送If-Modified-Since報頭,詢問Last-Modified時間點之後資源是否被修改過。如果沒有修改,則返回碼爲304,使用緩存;如果修改過,則再次去服務器請求資源,返回碼和首次請求相同爲200,資源爲服務器最新資源。

Etag & & If-None-Match

根據實體內容生成一段hash字符串,標識資源的狀態,由服務端產生。瀏覽器會將這串字符串傳回服務器,驗證資源是否已經修改,如果沒有修改,過程如下:
在這裏插入圖片描述

六、緩存報頭種類與優先級

  1. Cache-Control與Expires
    ache-Control與 Expires的作用一致,都是指明當前資源的有效期,控制瀏覽器是否直接從瀏覽器緩存取數據還是重新發請求到服務器取數據。只不過 Cache-Control的選擇更多,設置更細緻,如果同時設置的話,其優先級高於 Expires。

  2. Last-Modified與ETag
    Etag是服務器自動生成或者由開發者生成的對應資源在服務器端的唯一標識符,能夠更加準確的控制緩存。Last-Modified與ETag是可以一起使用的,服務器會優先驗證ETag,一致的情況下,纔會繼續比對Last-Modified,最後才決定是否返回304。

  3. Last-Modified/ETag 與 Cache-Control/Expires
    配置 Last-Modified/ETag的情況下,瀏覽器再次訪問統一URI的資源,還是會發送請求到服務器詢問文件是否已經修改,如果沒有,服務器會只發送一個304回給瀏覽器,告訴瀏覽器直接從自己本地的緩存取數據;如果修改過那就整個數據重新發給瀏覽器;

    Cache-Control/Expires則不同,如果檢測到本地的緩存還是有效的時間範圍內,瀏覽器直接使用本地副本,不會發送任何請求。兩者一起使用時, Cache-Control/Expires的優先級要高,即當本地副本根據 Cache-Control/Expires發現還在有效期內時,則不會再次發送請求去服務器詢問修改時間 Last-Modified或實體標識 Etag了。

    一般情況下,兩者會配合一起使用,因爲即使服務器設置緩存時間, 當用戶點擊“刷新”按鈕時,瀏覽器會忽略緩存繼續向服務器發送請求,這時 Last-Modified/ETag將能夠很好利用304,從而減少響應開銷。

強制緩存優先於協商緩存進行,若強制緩存(Expires和Cache-Control)生效則直接使用緩存,若不生效則進行協商緩存(Last-Modified / If-Modified-Since和Etag / If-None-Match),協商緩存由服務器決定是否使用緩存,若協商緩存失效,那麼代表該請求的緩存失效,返回200,重新返回資源和緩存標識,再存入瀏覽器緩存中;生效則返回304,繼續使用緩存。具體流程圖如下:
在這裏插入圖片描述

七、哪些請求不能被緩存?

  1. HTTP信息頭中包含Cache-Control:no-cache,pragma:no-cache,或Cache-Control:max-age=0等告訴瀏覽器不用緩存的請求

  2. 需要根據Cookie,認證信息等決定輸入內容的動態請求是不能被緩存的

  3. 經過HTTPS安全加密的請求(有人也經過測試發現,ie其實在頭部加入Cache-Control:max-age信息,firefox在頭部加入Cache-Control:Public之後,能夠對HTTPS的資源進行緩存,參考《HTTPS的七個誤解》)

  4. POST請求無法被緩存

  5. HTTP響應頭中不包含Last-Modified/Etag,也不包含Cache-Control/Expires的請求無法被緩存

八、用戶操作行爲與緩存的關係

用戶在使用瀏覽器的時候,會有各種操作,比如輸入地址後回車,按F5刷新等,這些行爲會對緩存有什麼影響呢?
在這裏插入圖片描述

不同的網頁打開方式

  1. 打開新窗口 – 如果指定cache-control的值爲private、no-cache、must-revalidate,那麼打開新窗口訪問時都會重新訪問服務器。而如果指定了max-age值,那麼在此值內的時間裏就不會重新訪問服務器,例如:
    Cache-control: max-age=5
    表示當訪問此網頁後的5秒內再次訪問不會去服務器
  2. 在地址欄回車 – 如果值爲private或must-revalidate(和網上說的不一樣),則只有第一次訪問時會訪問服務器,以後就不再訪問。如果值爲no-cache,那麼每次都會訪問。如果值爲max-age,則在過期之前不會重複訪問。
  3. 按後退按扭 – 如果值爲private、must-revalidate、max-age,則不會重訪問,而如果爲no-cache,則每次都重複訪問
  4. 按刷新按扭 – 無論爲何值,都會重複訪問

不同的刷新方式

通過上表我們可以看到,當用戶在按 F5進行刷新的時候,會忽略Expires/Cache-Control的設置,會再次發送請求去服務器請求,而Last-Modified/Etag還是有效的,服務器會根據情況判斷返回304還是200;

而當用戶使用 Ctrl+F5進行強制刷新的時候,只是所有的緩存機制都將失效,重新從服務器拉去資源。

  1. 普通刷新 – 當按下F5或者點擊刷新按鈕來刷新頁面的時候,瀏覽器將繞過本地緩存來發送請求到服務器, 此時, 協商緩存是有效的

  2. 強制刷新 – 當按下ctrl+F5來刷新頁面的時候, 瀏覽器將繞過各種緩存(本地緩存和協商緩存), 直接讓服務器返回最新的資源

  3. 回車或轉向 – 當在地址欄上輸入回車或者按下跳轉按鈕的時候, 所有緩存都生效

九、如何從緩存角度改善站點

  1. 同一個資源保證URL的穩定性

  2. 給css、js、圖片等資源增加HTTP緩存頭,並強制入口html不被緩存

  3. 減少對Cookie的依賴

  4. 多用Get方式請求動態Cgi

  5. 動態CGI也是可以被緩存

十、本地緩存

本地存儲主要有以下幾種,localStorage,sessionStorage,cookie和IndexDB,主要用在前端有大容量存儲需求的頁面上,例如,在線編輯瀏覽器或者網頁郵箱。他們都可以將數據存儲在瀏覽器,應該根據不同的場景進行使用。

  1. Cookie :
    主要是由服務器生成,且前端也可以設置,保存在客戶端本地的一個文件,通過response響應頭的set-Cookie字段進行設置,且Cookie的內容自動在請求的時候被傳遞給服務器。

  2. LocalStorage
    主要是前端開發人員,在前端設置,一旦數據保存在本地後,就可以避免再向服務器請求數據,因此減少不必要的數據請求,減少數據在瀏覽器和服務器間不必要地來回傳遞。
    可以長期存儲數據,沒有時間限制,一天,一年,兩年甚至更長,數據都可以使用,一般瀏覽器支持的是5M大小,這個在不同的瀏覽器中localStorage會有所不同。
    是一種本地存儲的公共資源,可長期且同源域名下資源可共享

  3. SessionStorage
    sessionStorage主要是前端開發人員,在前端設置,sessionStorage(會話存儲),只有在瀏覽器被關閉之前使用,創建另一個頁面時同意可以使用,關閉瀏覽器之後數據就會消失

  4. indexDB
    IndexedDB 就是瀏覽器提供的本地數據庫,它可以被網頁腳本創建和操作。IndexedDB 允許儲存大量數據,提供查找接口,還能建立索引。這些都是 LocalStorage 所不具備的。就數據庫類型而言,IndexedDB 不屬於關係型數據庫(不支持 SQL 查詢語句),更接近 NoSQL 數據庫。

參考鏈接:
https://segmentfault.com/a/1190000017962411?utm_source=tag-newest#articleHeader0
https://www.jianshu.com/p/54cc04190252

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