(轉載)徹底理解瀏覽器的緩存機制

来源:https://www.cnblogs.com/duiniweixiao/archive/2018/04/19/8884274.html
-Advertisement-
Play Games

徹底理解瀏覽器的緩存機制 2018/04/16 概述 瀏覽器的緩存機制也就是我們說的HTTP緩存機制,其機制是根據HTTP報文的緩存標識進行的,所以在分析瀏覽器緩存機制之前,我們先使用圖文簡單介紹一下HTTP報文,HTTP報文分為兩種: HTTP請求(Request)報文,報文格式為:請求行 – H ...


徹底理解瀏覽器的緩存機制

概述

瀏覽器的緩存機制也就是我們說的HTTP緩存機制,其機制是根據HTTP報文的緩存標識進行的,所以在分析瀏覽器緩存機制之前,我們先使用圖文簡單介紹一下HTTP報文,HTTP報文分為兩種:

  • HTTP請求(Request)報文,報文格式為:請求行 – HTTP頭(通用信息頭,請求頭,實體頭) – 請求報文主體(只有POST才有報文主體),如下圖
    Request
    Request
  • HTTP響應(Response)報文,報文格式為:狀態行 – HTTP頭(通用信息頭,響應頭,實體頭) – 響應報文主體,如下圖
    Response
    Response

註:通用信息頭指的是請求和響應報文都支持的頭域,分別為Cache-Control、Connection、Date、Pragma、Transfer-Encoding、Upgrade、Via;實體頭則是實體信息的實體頭域,分別為Allow、Content-Base、Content-Encoding、Content-Language、Content-Length、Content-Location、Content-MD5、Content-Range、Content-Type、Etag、Expires、Last-Modified、extension-header。這裡只是為了方便理解,將通用信息頭,響應頭/請求頭,實體頭都歸為了HTTP頭。

以上的概念在這裡我們不做多講解,只簡單介紹,有興趣的童鞋可以自行研究。

 

緩存過程分析

瀏覽器與伺服器通信的方式為應答模式,即是:瀏覽器發起HTTP請求 – 伺服器響應該請求。那麼瀏覽器第一次向伺服器發起該請求後拿到請求結果,會根據響應報文中HTTP頭的緩存標識,決定是否緩存結果,是則將請求結果和緩存標識存入瀏覽器緩存中,簡單的過程如下圖:
cache

由上圖我們可以知道:

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

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

以上兩點結論就是瀏覽器緩存機制的關鍵,他確保了每個請求的緩存存入與讀取,只要我們再理解瀏覽器緩存的使用規則,那麼所有的問題就迎刃而解了,本文也將圍繞著這點進行詳細分析。為了方便大家理解,這裡我們根據是否需要向伺服器重新發起HTTP請求將緩存過程分為兩個部分,分別是強制緩存協商緩存 

 

強制緩存

強制緩存就是向瀏覽器緩存查找該請求結果,並根據該結果的緩存規則來決定是否使用該緩存結果的過程,強制緩存的情況主要有三種(暫不分析協商緩存過程),如下:

  • 不存在該緩存結果和緩存標識,強制緩存失效,則直接向伺服器發起請求(跟第一次發起請求一致),如下圖:
    cache

  • 存在該緩存結果和緩存標識,但該結果已失效,強制緩存失效,則使用協商緩存(暫不分析),如下圖
    cache

  • 存在該緩存結果和緩存標識,且該結果尚未失效,強制緩存生效,直接返回該結果,如下圖
    cache

那麼強制緩存的緩存規則是什麼?

當瀏覽器向伺服器發起請求時,伺服器會將緩存規則放入HTTP響應報文的HTTP頭中和請求結果一起返回給瀏覽器,控制強制緩存的欄位分別是ExpiresCache-Control,其中Cache-Control優先順序比Expires高。

 

Expires

Expires是HTTP/1.0控制網頁緩存的欄位,其值為伺服器返回該請求結果緩存的到期時間,即再次發起該請求時,如果客戶端的時間小於Expires的值時,直接使用緩存結果。

Expires是HTTP/1.0的欄位,但是現在瀏覽器預設使用的是HTTP/1.1,那麼在HTTP/1.1中網頁緩存還是否由Expires控制?

到了HTTP/1.1,Expire已經被Cache-Control替代,原因在於Expires控制緩存的原理是使用客戶端的時間與服務端返回的時間做對比,那麼如果客戶端與服務端的時間因為某些原因(例如時區不同;客戶端和服務端有一方的時間不准確)發生誤差,那麼強制緩存則會直接失效,這樣的話強制緩存的存在則毫無意義,那麼Cache-Control又是如何控制的呢?

 

Cache-Control

在HTTP/1.1中,Cache-Control是最重要的規則,主要用於控制網頁緩存,主要取值為:

  • public:所有內容都將被緩存(客戶端和代理伺服器都可緩存)

  • private:所有內容只有客戶端可以緩存,Cache-Control的預設取值

  • no-cache:客戶端緩存內容,但是是否使用緩存則需要經過協商緩存來驗證決定

  • no-store:所有內容都不會被緩存,即不使用強制緩存,也不使用協商緩存

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

接下來,我們直接看一個例子,如下:
example

由上面的例子我們可以知道:

  • HTTP響應報文中expires的時間值,是一個絕對值

  • HTTP響應報文中Cache-Control為max-age=600,是相對值

由於Cache-Control的優先順序比expires,那麼直接根據Cache-Control的值進行緩存,意思就是說在600秒內再次發起該請求,則會直接使用緩存結果,強制緩存生效。

註:在無法確定客戶端的時間是否與服務端的時間同步的情況下,Cache-Control相比於expires是更好的選擇,所以同時存在時,只有Cache-Control生效。

瞭解強制緩存的過程後,我們拓展性的思考一下:

瀏覽器的緩存存放在哪裡,如何在瀏覽器中判斷強制緩存是否生效?

disk
這裡我們以博客的請求為例,狀態碼為灰色的請求則代表使用了強制緩存,請求對應的Size值則代表該緩存存放的位置,分別為from memory cache 和 from disk cache

那麼from memory cache 和 from disk cache又分別代表的是什麼呢?什麼時候會使用from disk cache,什麼時候會使用from memory cache呢?

from memory cache代表使用記憶體中的緩存,from disk cache則代表使用的是硬碟中的緩存,瀏覽器讀取緩存的順序為memory –> disk。

雖然我已經直接把結論說出來了,但是相信有不少人對此不能理解,那麼接下來我們一起詳細分析一下緩存讀取問題,這裡仍讓以我的博客為例進行分析:
訪問https://heyingye.github.io/ –> 200 –> 關閉博客的標簽頁 –> 重新打開https://heyingye.github.io/ –> 200(from disk cache) –> 刷新 –> 200(from memory cache)

過程如下:

看到這裡可能有人小伙伴問了,最後一個步驟刷新的時候,不是同時存在著from disk cache和from memory cache嗎?

對於這個問題,我們需要瞭解記憶體緩存(from memory cache)和硬碟緩存(from disk cache),如下:

  • 記憶體緩存(from memory cache):記憶體緩存具有兩個特點,分別是快速讀取時效性

    • 快速讀取:記憶體緩存會將編譯解析後的文件,直接存入該進程的記憶體中,占據該進程一定的記憶體資源,以方便下次運行使用時的快速讀取。

    • 時效性:一旦該進程關閉,則該進程的記憶體則會清空。

  • 硬碟緩存(from disk cache):硬碟緩存則是直接將緩存寫入硬碟文件中,讀取緩存需要對該緩存存放的硬碟文件進行I/O操作,然後重新解析該緩存內容,讀取複雜,速度比記憶體緩存慢。

在瀏覽器中,瀏覽器會在js和圖片等文件解析執行後直接存入記憶體緩存中,那麼當刷新頁面時只需直接從記憶體緩存中讀取(from memory cache);而css文件則會存入硬碟文件中,所以每次渲染頁面都需要從硬碟讀取緩存(from disk cache)。

 

協商緩存

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

  • 協商緩存生效,返回304,如下
    304

  • 協商緩存失效,返回200和請求結果結果,如下
    200

同樣,協商緩存的標識也是在響應報文的HTTP頭中和請求結果一起返回給瀏覽器的,控制協商緩存的欄位分別有:Last-Modified / If-Modified-Since和Etag / If-None-Match,其中Etag / If-None-Match的優先順序比Last-Modified / If-Modified-Since高。

 

Last-Modified / If-Modified-Since

  • Last-Modified是伺服器響應請求時,返回該資源文件在伺服器最後被修改的時間,如下。
    last-modify

  • If-Modified-Since則是客戶端再次發起該請求時,攜帶上次請求返回的Last-Modified值,通過此欄位值告訴伺服器該資源上次請求返回的最後被修改時間。伺服器收到該請求,發現請求頭含有If-Modified-Since欄位,則會根據If-Modified-Since的欄位值與該資源在伺服器的最後被修改時間做對比,若伺服器的資源最後被修改時間大於If-Modified-Since的欄位值,則重新返回資源,狀態碼為200;否則則返回304,代表資源無更新,可繼續使用緩存文件,如下。
    since

 

Etag / If-None-Match

  • Etag是伺服器響應請求時,返回當前資源文件的一個唯一標識(由伺服器生成),如下。
    Etag

  • If-None-Match是客戶端再次發起該請求時,攜帶上次請求返回的唯一標識Etag值,通過此欄位值告訴伺服器該資源上次請求返回的唯一標識值。伺服器收到該請求後,發現該請求頭中含有If-None-Match,則會根據If-None-Match的欄位值與該資源在伺服器的Etag值做對比,一致則返回304,代表資源無更新,繼續使用緩存文件;不一致則重新返回資源文件,狀態碼為200,如下。
    Etag-match

註:Etag / If-None-Match優先順序高於Last-Modified / If-Modified-Since,同時存在則只有Etag / If-None-Match生效。

 

總結

強制緩存優先於協商緩存進行,若強制緩存(Expires和Cache-Control)生效則直接使用緩存,若不生效則進行協商緩存(Last-Modified / If-Modified-Since和Etag / If-None-Match),協商緩存由伺服器決定是否使用緩存,若協商緩存失效,那麼代表該請求的緩存失效,重新獲取請求結果,再存入瀏覽器緩存中;生效則返回304,繼續使用緩存,主要過程如下:
all

轉自[徹底理解瀏覽器的緩存機制](https://heyingye.github.io/2018/04/16/%E5%BD%BB%E5%BA%95%E7%90%86%E8%A7%A3%E6%B5%8F%E8%A7%88%E5%99%A8%E7%9A%84%E7%BC%93%E5%AD%98%E6%9C%BA%E5%88%B6/)


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • tooltip.css 純CSS滑鼠提示工具。 v. 2.0.0 更新日期:2018.4.12 預覽DEMO。 ...
  • 含義 Promise 是非同步編程的一種解決方案,比傳統的解決方案——回調函數和事件——更合理和更強大。它由社區最早提出和實現,ES6 將其寫進了語言標準,統一了用法,原生提供了 Promise 對象。 Promise 對象是一個代理對象(代理一個值),被代理的值在 Promise 對象創建時可能是未 ...
  • 線程分為:單線程和多線程 單線程:一個正在運行的程式(即進行)至少有一個線程,這個線程叫做主線程,只有一個主線程的程式叫做單線程程式,主線程負責執行所有代碼的執行(UI展現及刷新、網路請求、本地存儲等),這些代碼只能順序執行,不能併發執行。 多線程:有多個線程的程式叫做多線程程式,主線程可以開闢多個 ...
  • $scope.yearList = [ {k : "2014", v : "2014"}, {k : "2015", v : "2015"}, {k : "2016", v : "2016"}, {k : "2017", v : "2017"}, ... ...
  • 本文章適合具有一定程式編程語言基礎的人士閱讀,最好學完Java基礎再來閱讀本文章更容易理解語言初學者會看起來比較費勁,不易理解 一.導入腳本 在html導入Javascript的格式是: 兩個關鍵屬性:deter,async deter:defer屬性告訴瀏覽器要等整個頁面載入以後、解析完畢才執行該 ...
  • Ionic基於AngularJS構建而成,所以學習一些AngularJS的知識很有必要。Ionic並沒有獨立開發一套完整的Web應用框架,而是對AngularJS進行了擴展,給它添加了大量界面組件和其他的移動端友好的特性。 1.index.html 首先會進入index頁面,裡面引入了angular ...
  • 預設為今年 var date = new Date; $scope.year = date.getFullYear(); //年份減 $scope.yearPrev = function(){ $scope.year -- ; ... ...
  • 1 文件上傳 2 3 4 5 6 7 1 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 在我們開發過程中基本上不可或缺的用到一些敏感機密數據,比如SQL伺服器的連接串或者是OAuth2的Secret等,這些敏感數據在代碼中是不太安全的,我們不應該在源代碼中存儲密碼和其他的敏感數據,一種推薦的方式是通過Asp.Net Core的機密管理器。 機密管理器 在 ASP.NET Core ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 順序棧的介面程式 目錄順序棧的介面程式頭文件創建順序棧入棧出棧利用棧將10進位轉16進位數驗證 頭文件 #include <stdio.h> #include <stdbool.h> #include <stdlib.h> 創建順序棧 // 指的是順序棧中的元素的數據類型,用戶可以根據需要進行修改 ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • C總結與剖析:關鍵字篇 -- <<C語言深度解剖>> 目錄C總結與剖析:關鍵字篇 -- <<C語言深度解剖>>程式的本質:二進位文件變數1.變數:記憶體上的某個位置開闢的空間2.變數的初始化3.為什麼要有變數4.局部變數與全局變數5.變數的大小由類型決定6.任何一個變數,記憶體賦值都是從低地址開始往高地 ...
  • 如果讓你來做一個有狀態流式應用的故障恢復,你會如何來做呢? 單機和多機會遇到什麼不同的問題? Flink Checkpoint 是做什麼用的?原理是什麼? ...
  • C++ 多級繼承 多級繼承是一種面向對象編程(OOP)特性,允許一個類從多個基類繼承屬性和方法。它使代碼更易於組織和維護,並促進代碼重用。 多級繼承的語法 在 C++ 中,使用 : 符號來指定繼承關係。多級繼承的語法如下: class DerivedClass : public BaseClass1 ...
  • 前言 什麼是SpringCloud? Spring Cloud 是一系列框架的有序集合,它利用 Spring Boot 的開發便利性簡化了分散式系統的開發,比如服務註冊、服務發現、網關、路由、鏈路追蹤等。Spring Cloud 並不是重覆造輪子,而是將市面上開發得比較好的模塊集成進去,進行封裝,從 ...
  • class_template 類模板和函數模板的定義和使用類似,我們已經進行了介紹。有時,有兩個或多個類,其功能是相同的,僅僅是數據類型不同。類模板用於實現類所需數據的類型參數化 template<class NameType, class AgeType> class Person { publi ...
  • 目錄system v IPC簡介共用記憶體需要用到的函數介面shmget函數--獲取對象IDshmat函數--獲得映射空間shmctl函數--釋放資源共用記憶體實現思路註意 system v IPC簡介 消息隊列、共用記憶體和信號量統稱為system v IPC(進程間通信機制),V是羅馬數字5,是UNI ...