瀏覽器資源快取策略
瀏覽器的快取大致分為四個方面
- Memory Cache(disk Cache)
- Service Worker Cache
- HTTP Cache
- Push Cache
HTTP Cache
- 瀏覽器在請求資源前會優先檢測本地是否有命中強快取。
- 強快取分expires與Cache-Control兩種。
- 如果命中強快取則會向 memory cache或disk cache等多種快取中去獲取。
- 如果沒有命中則會向服務端去請求資源。 HTTP快取分為強快取與協商快取強快取使用expires與Cache-Control等屬性來控制
強快取
expires
expires是HTTP/1.0時期提出的,主要是由服務端設定過期時間,然後瀏覽器通過對比本地時間與expires來確定資源是否過期是不是需要向服務端去索取資源。
expires: Wed, 11 Sep 2019 16:12:18 GMT
複製程式碼
缺點: expires是通過瀏覽器本地時間來對比的,如果人為的修改本地時間會導致資源快取強快取命中失敗,重新去獲取資源
Cache-Control
Cache-Control是HTTP/1.1作為expires全面的代替者提出的,通過對Cache-Control設定不同屬性來進行資源快取的判定
其屬性值有
- no-cache: 資料內容不能被瀏覽器快取, 每次請求都重新訪問伺服器確認是否過期(進行協商快取), 若有max-age, 則快取期間不訪問伺服器.
- no-store: 瀏覽器、伺服器、代理伺服器等全部不能快取,
- private(預設): 只能在瀏覽器中快取, 只有在第一次請求的時候才訪問伺服器, 若有max-age, 則快取期間不訪問伺服器.
- public: 可以被任何快取區快取, 如: 瀏覽器、伺服器、代理伺服器等
- max-age: 相對過期時間, 即以秒為單位的快取時間.
- s-max-age: 與max-age相似,兩者同時存在時優先使用s-max-age,並只在代理伺服器使用且只對public有效
- no-cache, private: 開啟新視窗時候重新訪問伺服器 , 若設定max-age, 則快取期間不訪問伺服器.
- private, 正數的max-age: 後退時候不會訪問伺服器
- no-cache, 正數的max-age: 後退時會訪問伺服器
cache-control: max-age=3600, s-maxage=31536000
複製程式碼
如果只設定了max-age,預設會採取public模式模式可以被全快取的
協商快取
協商快取是瀏覽器與伺服器之間的通訊,瀏覽器向伺服器詢問相關資訊,確定本地檔案是否過期是否需要向伺服器重新獲取資源,如果資源沒有變動則會重定向至瀏覽器端並且此時的Status Code為304 Not Modified
那麼協商快取快取是怎麼實現的呢?
- 通過Last-Modified與ETag來進行
Last-Modified
當cache-control為no-cahce時在響應頭上會帶上Last-Modified與ETag,同時Last-Modified是一個時間戳。
並且瀏覽器會在隨後的請求的請求頭上都攜帶上If-Modified-Since,這個If-Modified-Since也是一個時間戳,而這個If-Modified-Sincee也正是上次請求的資源中攜帶過來的Last-Modified,這個時候伺服器會對比該資源的時間戳跟伺服器上的資源的時間戳是否一樣,如果一致則返回304,重定向讓瀏覽器在快取中取得,如果不一致則重新返回檔案。 但是同樣這樣也是有弊端的- 如果修改了檔案但是沒有修改內容,這樣檔案的修改時間同樣會發生變化,同樣觸發校驗時則會導致觸發一次資源的重新請求
- 因為Last-Modified與If-Modified-Since都是使用時間戳來設定的,只要修改儲存的時間足夠快在100ms內完成了改動,則時間戳並不會變化,使得在校驗時無法檢驗出變化,沒有對新資源進行請求。
為了解決這樣的問題,ETag也作為Last-Modified的加強與補充出現了
ETag
ETag是伺服器給資源生成的標識字串,作為Last-Modified的加強與補充,他跟Last-Modified工作原理很相似,在資源的響應頭中生成ETag
,在後續請求的請求頭中會生成If-None-Match
,通過比對兩者的差異,如果Last-Modified與ETag同時存在時,對ETag進行優先判定
(對ETag與Last-Modified的補充)
Nginx下配置Last-Modified時(ETag是可配置的) Apache預設是兩者都會返回
那麼問題來了ETag作為Last-Modified的強化與補充如果只有ETag是否也會觸發互動?
結果是:ETag可以單獨使用,與服務端進行資源判定
HTTP快取指南
那對於這些資源設定應該是怎麼樣的呢?我們這裡參考一下Chrome給出的流程圖
首先我們需要確認資源是不是需要複用?如果不復用則將cache-control設定為no-store
,如果需要複用則將cache-control設定為no-cache
,隨後我們根據資源是不是需要被代理伺服器快取來設定public
或者private
,最後再設定max-age
;最後在設定Last-Modified
與ETag
等屬性。
MemoryCache與DiskCache
MemoryCache:
在伺服器記憶體空餘的時候優先使用Memory Cache隨後才會考慮使用disk Cache,
因為Memory Cache讀取速度是最快的同時也是最短命的在瀏覽器關閉該頁面時就會銷燬資源,disk Cache讀取速度比Memory Cache要慢但是由於它是存在硬碟中的所以它的存在時間是最長的也是最穩定的。
Service Worker Cache
上面介紹了httpcache又介紹了memoryCache,現在來介紹一下一個更陌生的service worker cache
。
我們書寫的js程式碼通常是在主執行緒執行,可以訪問DOM與Windows全域性變數,Service Worker與Web Worker則是獨立於主執行緒的JavaScript線層,因為他被設計成完全非同步的,所以她不會阻塞頁面渲染也不會阻塞JavaScript主流程的執行所以我們可以用它去快取離線資源,推送訊息等。
同時Service Worker對協議也是有要求的,必須是https,但是這個對於我們本地除錯或者開發其實是不友好的,不過還算Service Worker還算是人性化,可以再localhost跟127.0.0.1環境下執行,同時github也可以執行相關的程式碼。
這裡我們準備一個demo
ServiceWorker registration successful with scope: http://127.0.0.1:8080/
複製程式碼
再看cache Storage,則會對出一條資料,他就是用於儲存本地檔案,
我們重新請求頁面會發現serviceWorker.html檔案是從serviceworker中獲取的