從緩存位置上來說分為四種,并且各自有優先級,當依次查找緩存且都沒有命中的時候,才會去請求網絡。
Service Worker / Memory Cache / Disk Cache / Push Cache
Service Worker
Service Worker 是運行在瀏覽器后臺的獨立線程,可以用來實現緩存。由于 Service Worker 中涉及到請求攔截,所以必須是 Https協議的請求。Service Worker 的緩存與瀏覽器其他內建的緩存機制不同,它可以讓我們自由控制緩存哪些文件、如何匹配緩存、如何讀取緩存,并且緩存是持續性的。
當 Service Worker 沒有命中緩存的時候,我們需要去調用 fetch 函數獲取數據。也就是說,如果我們沒有在 Service Worker 命中緩存的話,會根據緩存查找優先級去查找數據。但是不管我們是從 Memory Cache 中還是從網絡請求中獲取的數據,瀏覽器都會顯示我們是從 Service Worker 中獲取的內容。
Service Worker 實現緩存一般分為三步:
首先注冊 Service Worker;
監聽到 install 事件后緩存需要的文件;
用戶下次請求的時候查詢是否存在緩存,存在直接讀取緩存文件,否則就去請求數據。
Memory Cache
內存中的緩存。一般網頁中已經抓取到的資源會被放入內存緩存,比如已經下載的樣式、腳本、圖片等等。
內存緩存雖然讀取高效,可是緩存持續性很短,會隨著進程的釋放而釋放。一般我們關閉tab頁面以后緩存就會被釋放。
內存緩存在緩存資源時并不關心返回資源的HTTP緩存頭Cache-Control是什么值,同時資源的匹配也并非僅僅是對URL做匹配,還可能會對Content-Type,CORS等其他特征做校驗。
內存緩存中有一塊重要的緩存資源是preloader相關指令,例如<link rel="preload" href="sintel-short.mp4">,
preload預加載也是前端優化的常用手段。
Disk Cache
硬盤中的緩存。相比內存緩存讀取速度更慢,但是沒有任何限制,容量和生命周期都更好。它會根據 HTTP Herder 中的字段判斷哪些資源需要緩存,哪些資源可以不請求直接使用,哪些資源已經過期需要重新請求。
瀏覽器對 memory Cache 和 disk cache 存儲優先級:
* 對于大文件來說,大概率是不存儲在內存中的,反之優先
* 當前系統內存使用率高的話,文件優先存儲進硬盤
Push Cache
推送緩存是Http2.0才有的,當以上的緩存沒有命中才會被使用。他只會在 Session 中存在,一旦會話結束就被釋放,緩存時間也很短暫。在Chrome瀏覽器中只有5分鐘左右,同時它也并非嚴格執行HTTP頭中的緩存指令。
關于推送緩存的一特性:
* 所有的資源都能被推送,并且能夠被緩存,但是 Edge 和 Safari 瀏覽器支持相對比較差
* 可以推送 no-cache 和 no-store 的資源
* 一旦連接被關閉,Push Cache 就被釋放
* 多個頁面可以使用同一個HTTP/2的連接,也就可以使用同一個Push Cache。這主要還是依賴瀏覽器的實現而定,出于對性能的考慮,有的瀏覽器會對相同域名但不同的tab標簽使用同一個HTTP連接。
* Push Cache 中的緩存只能被使用一次
* 瀏覽器可以拒絕接受已經存在的資源推送
* 你可以給其他域名推送資源
緩存過程
1.瀏覽器每次發起請求,都會先在瀏覽器緩存中查找該請求的結果以及緩存標識
2.瀏覽器每次拿到返回的請求結果都會將該結果和緩存標識存入瀏覽器緩存中
根據是否需要向服務器重新發起HTTP請求,分為 強緩存和*協商緩存*兩種緩存策略
緩存策略
瀏覽器緩存策略分為兩類:強緩存和協商緩存,緩存策略通過 HTTP 頭部 Header 來設置。
強緩存
不會向服務器發送請求,直接從緩存中讀取資源,在chrome控制臺的Network選項中可以看到該請求返回200的狀態碼,并且Size顯示from disk cache或from memory cache。強緩存可以通過設置兩種 HTTP Header 實現:Expires 和Cache-Control。
Cache-Control
可以通過 Cache-Ccontrol 控制緩存的工作機制。
--指令--說明public所有內容都將被緩存private所有內容只有客戶端會被緩存,中間節點不允許緩存no-cache客戶端緩存內容,使用緩存則需要經過協商緩存來驗證決定no-store所有內容都不會被緩存,即不使用強制緩存,也不使用協商緩存max-agemax-age=xxx (xxx is numeric)表示緩存內容將在xxx秒后失效s-maxage同max-age作用一樣,只在代理服務器中生效(比如CDN緩存),如果存在s-maxage,則會覆蓋掉max-age和Expires headermax-stale能容忍的最大過期時間,如果沒有指定,那么說明瀏覽器愿意接收任何age的響應min-fresh能夠容忍的最小新鮮度,min-fresh標示了客戶端不愿意接受新鮮度不多于當前的age加上min-fresh設定的時間之和的響應。可以結合多個指令,實現不同的緩存功能:
Expires
指定緩存資源的過期時間(),Expires 是服務器響應實體首部字段。如果在 Cache-Control 響應頭設置了 "max-age" 或者 "s-max-age" 指令,那么 Expires 頭會被忽略。同時注意,如果修改本地時間打過 Expires 的時間會造成緩存失效。
示例:
Expires: Wed, 21 Oct 2015 07:28:00 GMT
兩者對比
兩者差別不大,區別就在于 Expires 是http1.0的產物,Cache-Control 是http1.1的產物,兩者同時存在的話, Cache-Control 優先級高于 Expires ,現階段 Expires 更多的是用于兼容的寫法。
協商緩存
協商緩存就是強制緩存失效后,瀏覽器攜帶緩存標識向服務器發起請求,由服務器根據緩存標識決定是否使用緩存的過程。協商返回會有兩種狀態:
1.協商緩存生效,返回 304 Not Modified 狀態碼:
2.協商緩存失敗,返回 200 狀態碼:
協商緩存可以通過設置 Last-Modified 和 ETag 實現:
Last-Modified
該字段為響應實體的頭部字段,指明資源在服務器上最后的修改時間。
過程:
- 瀏覽器接受有 Last-Modified 字段的響應后會緩存文件和header。
- 瀏覽器再次請求這個資源的時候,檢測到有 Last-Modified 這個header,然后在header中添加 If-Modified-Since 這個字段,值為 Last-Modified 的值。
- 服務器再次收到這個資源請求,會根據 If-Modified-Since 中的值與服務器中這個資源的最后修改時間對比,如果沒有變化,返回304和空的響應體,直接從緩存讀取,如果 If-Modified-Since 的時間小于服務器中這個資源的最后修改時間,說明文件有更新,于是返回新的資源文件和200。
缺點:
- 如果本地打開緩存文件,即使沒有對文件進行修改,但還是會造成 Last-Modified 被修改,服務端不能命中緩存導致發送相同的資源。
- 因為 Last-Modified 只能以秒計時,如果在不可感知的時間內(毫秒級)修改完成文件,那么服務端會認為資源還是命中了,不會返回正確的資源。
ETag
Etag是服務器響應請求時,返回當前資源文件的一個唯一標識(由服務器生成),只要資源有變化,Etag就會重新生成。
瀏覽器在下一次加載資源向服務器發送請求時,會將上一次返回的 Etag 值放到request header里的If-None-Match里,服務器只需要比較客戶端傳來的 If-None-Match 跟自己服務器上該資源的ETag是否一致,就能很好地判斷資源相對客戶端而言是否被修改過了。
兩者對比
- 精確度上,Etag 要優于 Last-Modified,Last-Modified 單位是秒,可能文件一秒內更新了多次而沒有返回,并且負載均衡的服務器生成的 Last-Modified 也可能不一樣。
- 性能上,Last-Modified 要優于 Etag,因為 Last-Modified 只需要記錄時間,而 Etag 還需要計算出一個 hash值。
- 優先級上,服務器會優先考慮 Etag。
緩存機制
- 強制緩存優先于協商緩存進行,若強制緩存(Expires和Cache-Control)生效則直接使用緩存;
- 若強緩存不生效則進行協商緩存(Last-Modified / If-Modified-Since和Etag / If-None-Match);
- 協商緩存由服務器決定是否使用緩存,若協商緩存失效,那么代表該請求的緩存失效,返回200,重新返回資源和緩存標識,再存入瀏覽器緩存中;生效則返回304,繼續使用緩存;
- 若什么策略都沒有,瀏覽器會采用一個啟發式算法,通常會取響應頭中的 Date 減去 Last-Modified 值的 10% 作為緩存時間。