常用的緩存
Memory Cache
Memory Cache 也就是內存中的緩存,主要包含的是當前中頁面中已經抓取到的資源,例如頁面上已經下載的樣式、腳本、圖片等。讀取內存中的數據肯定比磁盤快,內存緩存雖然讀取高效,可是緩存持續性很短,會隨着進程的釋放而釋放。 一旦我們關閉 Tab 頁面,內存中的緩存也就被釋放了。
那麼既然內存緩存這麼高效,我們是不是能讓數據都存放在內存中呢?
這是不可能的。計算機中的內存一定比硬盤容量小得多,操作系統需要精打細算內存的使用,所以能讓我們使用的內存必然不多。
需要注意的事情是,內存緩存在緩存資源時並不關心返回資源的HTTP緩存頭Cache-Control是什麼值,同時資源的匹配也並非僅僅是對URL做匹配,還可能會對Content-Type,CORS等其他特徵做校驗。
Disk Cache
Disk Cache 也就是存儲在硬盤中的緩存,讀取速度慢點,但是什麼都能存儲到磁盤中,比之 Memory Cache 勝在容量和存儲時效性上。
在所有瀏覽器緩存中,Disk Cache 覆蓋面基本是最大的。它會根據 HTTP Herder 中的字段判斷哪些資源需要緩存,哪些資源可以不請求直接使用,哪些資源已經過期需要重新請求。並且即使在跨站點的情況下,相同地址的資源一旦被硬盤緩存下來,就不會再次去請求數據。絕大部分的緩存都來自 Disk Cache
瀏覽器會把哪些文件丟進內存中?哪些丟進硬盤中?
關於這點,網上説法不一,不過以下觀點比較靠得住:
對於大文件來説,大概率是不存儲在內存中的,反之優先
當前系統內存使用率高的話,文件優先存儲進硬盤
資源本身大小數值
當http狀態為200是實實在在從瀏覽器獲取的資源,當http狀態為304時該數字是與服務端通信報文的大小,並不是該資源本身的大小,該資源是從本地獲取的
chrome採取措施的準則
什麼時候是from memory cache 什麼時候是from disk cache 呢?
即哪些資源會放在內存當中,哪些資源瀏覽器會放在磁盤上呢,結果如下下表所示
| 狀態 | 類型 | 説明 |
| 200 | form memory cache | <p>不請求網絡資源,資源在內存當中,一般腳本、字體、圖片會存在內存當中</p> |
| 200 | form disk ceche | 不請求網絡資源,在磁盤當中,一般非腳本會存在內存當中,如css等 |
| 200 | 資源大小數值 | 從服務器下載最新資源 |
| 304 | 報文大小 | 請求服務端發現資源沒有更新,使用本地資源 |
不同瀏覽器策略是否一致
以上的數據及統計都是在chrome瀏覽器下進行的
在Firefox下並沒有from memory cache以及from disk cache的狀態展現
相同的資源在chrome下是from disk/memory cache,但是Firefox統統是304狀態碼
即Firefox下會緩存資源,但是每次都會請求服務器對比當前緩存是否更改,chrome不請求服務器,直接拿過來用
這也是為啥chrome比較快的原因之一吧,
強緩存
強緩存:不會向服務器發送請求,直接從緩存中讀取資源,在chrome控制枱的Network選項中可以看到該請求返回200的狀態碼,並且Size顯示from disk cache或from memory cache。強緩存可以通過設置兩種 HTTP Header 實現:Expires 和 Cache-Control。
1.Expires
緩存過期時間,用來指定資源到期的時間,是服務器端的具體的時間點。也就是説,Expires=過期時間。Expires是Web服務器響應消息頭字段,在響應http請求時告訴瀏覽器在過期時間前瀏覽器可以直接從瀏覽器緩存取數據,而無需再次請求。
Expires 是 HTTP/1 的產物,受限於本地時間,如果修改了本地時間,可能會造成緩存失效。Expires: Wed, 22 Oct 2018 08:41:00 GMT表示資源會在 Wed, 22 Oct 2018 08:41:00 GMT 後過期,需要再次請求。
2.Cache-Control
在HTTP/1.1中,Cache-Control是最重要的規則,主要用於控制網頁緩存。比如當Cache-Control:max-age=300時,則代表在這個請求正確返回時間(瀏覽器也會記錄下來)的5分鐘內再次加載資源,就會命中強緩存。
3.Expires和Cache-Control兩者對比
其實這兩者差別不大,區別就在於 Expires 是http1.0的產物,Cache-Control是http1.1的產物,兩者同時存在的話,Cache-Control優先級高於Expires;在某些不支持HTTP1.1的環境下,Expires就會發揮用處。所以Expires其實是過時的產物,現階段它的存在只是一種兼容性的寫法。
強緩存判斷是否緩存的依據來自於是否超出某個時間或者某個時間段,而不關心服務器端文件是否已經更新
協商緩存
協商緩存就是強制緩存失效後,瀏覽器攜帶緩存標識向服務器發起請求,由服務器根據緩存標識決定是否使用緩存的過程,主要有以下兩種情況:、
1協商緩存生效,返回304 Not Modified,瀏覽器會去使用協商緩存
2協商緩存失效,返回200和最小請求結果
協商緩存可以通過設置兩種 HTTP Header 實現:Last-Modified 和 ETag
請求示例
返回示例
1.Last-Modified和If-Modified-Since
瀏覽器在第一次訪問資源時,服務器返回資源的同時,在response header中添加 Last-Modified的header,值是這個資源在服務器上的最後修改時間,瀏覽器接收後緩存文件和header;
Last-Modified: Fri, 22 Jul 2016 01:47:00 GMT
瀏覽器下一次請求這個資源,瀏覽器檢測到有 Last-Modified這個header,於是添加If-Modified-Since這個header,值就是Last-Modified中的值;服務器再次收到這個資源請求,後台代碼會根據不同情況進行控制,會根據 If-Modified-Since 中的值與服務器中這個資源的最後修改時間對比,如果沒有變化,返回304和空的響應體,直接從緩存讀取,如果If-Modified-Since的時間小於服務器中這個資源的最後修改時間,説明文件有更新,於是返回新的資源文件和200
2.ETag和If-None-Match
Etag是服務器響應請求時,返回當前資源文件的一個唯一標識(由服務器生成),只要資源有變化,Etag就會重新生成。瀏覽器在下一次加載資源向服務器發送請求時,後台代碼會根據不同情況進行控制,會將上一次返回的Etag值放到request header裏的If-None-Match裏,服務器只需要比較客户端傳來的If-None-Match跟自己服務器上該資源的ETag是否一致,就能很好地判斷資源相對客户端而言是否被修改過了。如果服務器發現ETag匹配不上,那麼直接以常規GET 200回包形式將新的資源(當然也包括了新的ETag)發給客户端;如果ETag是一致的,則直接返回304知會客户端直接使用本地緩存即可
兩者之間對比:
首先在精確度上,Etag要優於Last-Modified。
Last-Modified的時間單位是秒,如果某個文件在1秒內改變了多次,那麼他們的Last-Modified其實並沒有體現出來修改,但是Etag每次都會改變確保了精度;如果是負載均衡的服務器,各個服務器生成的Last-Modified也有可能不一致。
第二在性能上,Etag要遜於Last-Modified,畢竟Last-Modified只需要記錄時間,而Etag需要服務器通過算法來計算出一個hash值。
第三在優先級上,服務器校驗優先考慮Etag
實際場景應用緩存策略
1.頻繁變動的資源
Cache-Control: no-cache
對於頻繁變動的資源,在請求頭配置Cache-Control: no-cache 使瀏覽器每次都請求服務器,然後配合 ETag 或者 Last-Modified 來驗證資源是否有效。這樣的做法雖然不能節省請求數量,但是能顯著減少響應數據大小。
2.不常變化的資源
Cache-Control: max-age=31536000
通常在處理這類資源時,給響應頭的 Cache-Control 配置一個很大的 max-age=31536000 (一年),這樣瀏覽器之後請求相同的 URL 會命中強制緩存。而為了解決更新的問題,就需要在文件名(或者路徑)中添加 hash, 版本號等動態字符,之後更改動態字符,從而達到更改引用 URL 的目的,讓之前的強制緩存失效 (其實並未立即失效,只是不再使用了而已)。
在線提供的類庫 (如 jquery-3.3.1.min.js, lodash.min.js 等) 均採用這個模式。
用户行為對瀏覽器緩存的影響
所謂用户行為對瀏覽器緩存的影響,指的就是用户在瀏覽器如何操作時,會觸發怎樣的緩存策略。主要有 3 種:
打開網頁,地址欄輸入地址: 查找 disk cache 中是否有匹配。如有則使用;如沒有則發送網絡請求。
普通刷新 (F5):因為 TAB 並沒有關閉,因此 memory cache 是可用的,會被優先使用(如果匹配的話)。其次才是 disk cache。
強制刷新 (Ctrl + F5):瀏覽器不使用緩存(包括協商緩存與強緩存),因此發送的請求頭部均帶有 Cache-control: no-cache(為了兼容,還帶了 Pragma: no-cache),服務器直接返回 200 和最新內容。