前幾天,有個朋友問了我一個問題,他說本身的html設置了cache-control的max-age,爲何chrome瀏覽器第一次加載的時候是200,以後就走了304。針對這個問題,引出瞭如下知識。html
緩存機制是提升資源使用效率最有效的方法。它的思想就是創建一個資源池,webkit請求資源的時候,先從資源池中尋找目標文件,若是有則使用,若是沒有則經過網絡請求。 瀏覽器常見的緩存分爲強緩存和協商緩存。下面咱們用一張流程圖來介紹一下強緩存和協商緩存。前端
強緩存:瀏覽器不發送任何請求,直接從本地緩存中讀取文件。Status Code: 200 OK。web
協商緩存:瀏覽器發送請求,經過服務器來斷定是否讀取本地緩存。Status Code:304 Not Modified。算法
咱們簡單說一下流程圖。 瀏覽器發送請求時,會判斷當前環境是否存在緩存。若是不存在緩存,則發起請求,客戶端完成接受及緩存工做。若是存在緩存,則先判斷緩存是否過時。chrome
那麼怎麼斷定緩存是否過時呢?瀏覽器
主要有兩個字段,一個是Expires
,一個是Cache-Control
。緩存
Expires
是Http1.0的規範,它是一個絕對時間的字符串,告知瀏覽器在該時間以前可以使用資源。服務器
Cache-Control
是Http1.1的規範,主要利用max-age
設置一個相對時間表明過時時間。Cache-Control
也可設置其它值,經常使用的就是max-age
及no-cache
。網絡
所以,經過Cache-Control
完成緩存是否過時的判斷,若是沒有過時,則讀取本地緩存文件,此時完成的是強緩存的讀取。若是已通過期,則強緩存讀取失敗,進入協商緩存階段。字體
那麼協商緩存是怎麼處理的呢?
協商緩存主要有兩對值,分別爲Etag
/If-None-Match
(默認是對文件內容計算獲得的Hash值)和Last-Modified
/If-Modified-Since
(瀏覽器向服務器發送資源最後的修改時間),他們是成對出現的。
Etag
是Http1.1規範,是由服務器生成返回給前端。在協商緩存時,若是存在Etag
,則請求服務器時會帶上If-None-Match
,這個If-None-Match
的值就是服務器返回給前端的Etag
的值。
Last-Modified
是Http1.0規範,是文件在服務器端最後被修改的時間。在協商緩存時,若是Last-Modified
存在,則請求服務器時會攜帶if-modified-since
,該字段爲上次向服務端請求時返回的Last-Modified
。
若是以上兩對至少存在一對,則瀏覽器向服務器發送請求,並攜帶相關字段,交由服務器斷定是否進行協商緩存。在服務端,Etag
的優先級要高於Last-Modified
。所以服務器先進入Etag
的判斷,再判斷Last-Modified
。若是判斷經過,則返回304,瀏覽器讀取本地緩存。若是判斷未經過,則返回200即文件資源,瀏覽器接受並從新完成緩存。
以上就是整個瀏覽器請求時,關於緩存的一系列操做。
那麼Webkit內部是如何實現緩存機制的呢?咱們都知道,資源加載是網頁加載和渲染的第一步,也是最爲關鍵的一步。網頁中依賴的資源有不少,一般包括HTML/JS/CSS/圖片/SVG/視頻等文件,這些類型的文件在Webkit內部都有不一樣的類來表示。Webkit主要包含如下資源類:
CachedResource
。其中,HTML屬於
MainResource
類,與之對應的是
CachedRawResource
類。
爲何全部的類前面都帶有Cached呢?由於全部對資源的請求,默認都先走緩存,再決定是否向服務器發起請求。這個緩存機制的思想就是經過創建一個緩存池,當Webkit須要資源的時候,默認去緩存池中尋找。
靠什麼尋找?靠的是URL來尋找,正好符合了URL統一資源定位符的氣質。若是匹配則使用,若是不匹配則建立CachedResource
下的一個對象,例如圖片則建立CachedIamge
對象,並經過網絡模塊獲取。此時,咱們指的這個緩存就是內存緩存,也就是咱們在瀏覽器中見到的200 from memory,那麼何時是200 from disk呢?後面咱們再來介紹。
上面說到了Webkit中的資源,那怎麼加載資源,確定得有相應的加載器。
Webkit主要有三種加載器。
第一種就是特定的加載器,好比加載image就有特定的圖片加載器,加載js就有特定的js加載器,加載字體就要特定的字體加載器;
第二種是緩存機制的資源加載器,就是第一種特定加載器先來緩存機制的資源加載器中尋找緩存資源;
第三種是通用的資源加載器,是在Webkit須要從網絡或者文件系統獲取資源時的加載器。
說到第三種加載器,它是從網絡或者文件系統獲取資源,那麼咱們想說的200 from disk也就是在此發生。即from disk 發生在 from memory以後。爲何咱們說from disk是經過網絡模塊獲取呢?由於在Webkit中,網絡模塊包含磁盤緩存。
說到這咱們簡單說下from disk和from memory。
from memory,字面意思就是來自內存,其實這裏表達的也就是字面的意思。指的是咱們當前加載的這個資源是從內存中獲取的。這種請求不走瀏覽器,關閉頁面時,內存會釋放,再次打開也不會出現from memory。
from disk,也是字面意思,就是資源來自磁盤。來自磁盤中的數據確定是咱們以前緩存過得,它不會隨着瀏覽器的關閉而消失,咱們每每打開瀏覽器時會出現的就是from disk。
因此順序就是from memory => from disk => http request
爲何咱們刷新頁面的時候在network有些是from memory有些是from disk而有些是從新加載呢?
由於Webkit中的緩存池確定是有生命週期的,不可能讓緩存池一直變大,不然就會內存溢出。所以,必須有相應的機制來控制緩存池的大小。Webkit採用的是LRU算法即最近最少使用算法。該算法是內存管理常見的頁面置換算法,它選擇最近最久未使用的頁面予以淘汰,以此來保障資源池的大小。咱們說的這個資源池指的是內存緩存的資源池。那麼硬盤緩存呢?
硬盤緩存中,Webkit會維護一個緩存資源表,該表中的關鍵字就是URL。根據瀏覽器的狀況來更新這個緩存表。同時,Webkit也使用LRU算法來控制這張表的大小。
以上就是Webkit中的基本緩存策略,那麼咱們回到文章開頭提到的那個問題,爲何在Chrom瀏覽器中是304而不是200呢?查閱了相關的資料,找到了這樣一句話。
In particular, main resource loads do not get the benefits of WebKit’s memory cache.
這裏講到的main resource就跟咱們前面介紹的HTML屬於MainResource
類同樣,HTML文件並不走memory cache,所以在Chrome瀏覽器中會出現304,而不會出現200。其實,咱們能夠認爲這是Webkit替咱們往前邁了一步。在目前咱們開發網頁時,一般不會給HTML設置緩存或者設置很短期的緩存,以保證文件的及時更新,而Webkit走在了最前面。咱們在此說的是Webkit,像FireFox瀏覽器則不是這樣。