我理解的瀏覽器快取策略

南風知白牆發表於2019-12-26

瀏覽器資源快取策略

瀏覽器的快取大致分為四個方面

  • 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-ModifiedETag,同時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也是有其弊端,不同於檔案修改系統預設會記錄其修改的時間,ETag的生成是需要額外的開銷,大量的資源生成會影響效能,如果沒有特殊要求使用Last-Modified已經可以滿足大多數需求了。

(對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-ModifiedETag等屬性。

MemoryCache與DiskCache

MemoryCache:在伺服器記憶體空餘的時候優先使用Memory Cache隨後才會考慮使用disk Cache, 因為Memory Cache讀取速度是最快的同時也是最短命的在瀏覽器關閉該頁面時就會銷燬資源,disk Cache讀取速度比Memory Cache要慢但是由於它是存在硬碟中的所以它的存在時間是最長的也是最穩定的。

我理解的瀏覽器快取策略
如圖在我關閉原有頁面後重新開啟該頁面,沒有從memory cache中去獲取的資源,都是從disk 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

我理解的瀏覽器快取策略

我理解的瀏覽器快取策略
我們啟一個伺服器(這裡我推薦使用live-server) 頁面第一次開啟時Application下的cache Storage是沒有任何檔案的,但是在看到控制檯中顯示

ServiceWorker registration successful with scope:  http://127.0.0.1:8080/
複製程式碼

我理解的瀏覽器快取策略
再看cache Storage,則會對出一條資料,他就是用於儲存本地檔案, 我們重新請求頁面會發現serviceWorker.html檔案是從serviceworker中獲取的

我理解的瀏覽器快取策略

我理解的瀏覽器快取策略

Push Cache

【持續更新中....】

參考文獻 藉助Service Worker和cacheStorage快取及離線開發 前端效能優化原理與實踐

相關文章