客戶端緩存分為Http緩存和本地緩存,使用緩存好處很多,例如減少相同數據的重復傳輸,節省網絡帶寬資源緩解網絡瓶頸,降低了對原始服務器的要求,避免出現過載,這樣服務器可以更快響應其他的請求
Http緩存
http緩存分文強制緩存和協商緩存,主要用來在客戶端存儲一些不經常變化的的靜態文件,像圖片、CSS、JS等。在講強制緩存和協商緩存之前,先了解一下Http緩存的過程:
強制緩存
瀏覽器在請求某一個資源時,會先獲取資源的header信息,判斷是否命中強緩存(cache-control和expires信息),若命中,則直接從緩存中獲取資源信息,包括header信息,本次請求就不會與服務器通信。
緩存方案
Expires:Expires(/ɪkˈspaɪərz/),到期的意思,response header里的過期時間,瀏覽器再次加載資源時,如果在這個過期時間內,則命中強緩存。
Cache-Control:當值設為max-age=300時,則代表在這個請求正確返回時間(瀏覽器也會記錄下來)的5分鍾內再次加載資源,就會命中強緩存。
Cache-Control與Expires的作用一致,都是指明當前資源的有效期,控制瀏覽器是否直接從瀏覽器緩存取數據還是重新發請求到服務器取數據。只不過Cache-Control的選擇更多,設置更細致,如果同時設置的話,其優先級高於Expires
cache-control除了該字段外,還有下面幾個比較常用的設置值:
public、private、no-cache、no- store、no-transform、must-revalidate、proxy-revalidate、max-age等等
各個消息中的指令含義如下:
Public指示響應可被任何緩存區緩存。
Private指示對於單個用戶的整個或部分響應消息,不能被共享緩存處理。這允許服務器僅僅描述當用戶的部分響應消息,此響應消息對於其他用戶的請求無效。
no-cache指示請求或響應消息不能緩存
no-store用於防止重要的信息被無意的發布。在請求消息中發送將使得請求和響應消息都不使用緩存。
max-age指示客戶機可以接收生存期不大於指定時間(以秒為單位)的響應。
min-fresh指示客戶機可以接收響應時間小於當前時間加上指定時間的響應。
max-stale指示客戶機可以接收超出超時期間的響應消息。如果指定max-stale消息的值,那么客戶機可以接收超出超時期指定值之內的響應消息。
協商緩存
如果沒有命中強緩存,瀏覽器會發送請求到服務器,請求會攜帶第一次返回的有關緩存的header字段信息(Last-Modifued/If-Modified-Since和Etag/If-None-Match),由服務器根據header信息來比對結果是否協商緩存命中。若命中,則服務器返回新的響應header信息更新緩存中的對應header信息,但是不返回資源內容,它會告知瀏覽器可以直接從緩存獲取;否則返回最新的資源內容。
緩存方案
Last-Modify/If-Modify-Since:瀏覽器第一次請求一個資源的時候,服務器返回的header中會加上Last-Modify,Last-modify是一個時間標識該資源的最后修改時間;當瀏覽器再次請求該資源時,request的請求頭中會包含If-Modify-Since,該值為緩存之前返回的Last-Modify。服務器收到If-Modify-Since后,根據資源的最后修改時間判斷是否命中緩存
Etag/If-None-Match:web服務器響應請求時,告訴瀏覽器當前資源在服務器的唯一標識(etag)(生成規則由服務器決定)。當資源過期時(使用Cache-Control標識的max-age),發現資源具有Etag聲明,則再次向web服務器請求時帶上頭If-None-Match (Etag的值)。web服務器收到請求后發現有頭If-None-Match 則與被請求資源的相應校驗串進行比對,決定是否命中協商緩存;
Last-Modified和Etag區別
Last-Modified和Etag的區別 在描述中對於Last-Modified和Etag的使用可以說是差不多的,但是,Etag(http1.1)的出現是為了解決幾個Last-Modified比較難解決的問題:
- 一些文件也許會周期性的更改,但是他的內容並不改變(僅僅改變的修改時間),這個時候我們並不希望客戶端認為這個文件被修改了,而重新GET
- 某些文件修改非常頻繁,比如在秒以下的時間內進行修改,(比方說1s內修改了N次),If-Modified-Since能檢查到的粒度是s級的,這種修改無法判斷(或者說UNIX記錄MTIME只能精確到秒)
- 某些服務器不能精確的得到文件的最后修改時間 這時,利用Etag能夠更加准確的控制緩存,因為Etag是服務器自動生成或者由開發者生成的對應資源在服務器端的唯一標識符
Last-Modified與ETag是可以一起使用的,服務器會優先驗證ETag,一致的情況下,才會繼續比對Last-Modified,最后才決定是否返回304。
強緩存與協商緩存的區別
獲取資源方式 | 狀態碼 | 是否發送到服務器 | |
---|---|---|---|
強緩存 | 緩存中獲取 | 200(from cache) | 否,直接從緩存中獲取 |
協商緩存 | 緩存中獲取 | 304(not modify) | 是,通過服務器來告知緩存是否可用 |
本地緩存
本地緩存和瀏覽器有很大關系,可能會出現不同的瀏覽器適用的緩存技術不同,常見的本地緩存:
cookie
- 1.兼容所有的瀏覽器
- 2.有存儲的大小限制,一般一個源(一個域下)只能存儲4KB內容
- 3.cookie有過期時間(當然我們自己可以手動設置這個時間)
- 4.殺毒軟件或者瀏覽器的垃圾清理都可能會把cookie信息強制清除掉
- 5.在隱私或者無痕瀏覽模式下,是不記錄cookie的
- 6.cookie不是嚴格的本地存儲,因為要和服務器之間來回傳輸
Chrome瀏覽器緩存的Cookie緩存
localStorage
- 1.不兼容IE8及以下
- 2.也有存儲的大小限制,一個源下最多只能存儲5MB左右
- 3.本地永久存儲,只要你不手動刪除,永遠存儲在本地(但是我們可以基於API removeItem/clear手動清除一些自己想要刪除的信息)
- 4.殺毒軟件或者瀏覽器的垃圾清理暫時不會清除localStorage(新版本谷歌瀏覽器會清除localStorage等信息)
- 5.在隱私或者無痕瀏覽模式下,是記錄localStorage的
- 6.localStorage和服務器沒有半毛錢關系
sessionStorage
- sessionStorage 和localStorage 唯一的區別在於sessionStorage 是臨時存儲,只對當前回話有效,當瀏覽器當前標簽頁關閉則失效,與localStorage 擁有同樣的方法。
localStorageh和sessionStorage 都只擁有大約5M的存儲空間,不適用於存儲大數據量數據。對於數據量較大的數據緩存,我們應該應用本地數據庫實現(indexDB)
indexDB
IndexedDB 具有以下特點:
(1)鍵值對儲存。
IndexedDB 內部采用對象倉庫(object store)存放數據。所有類型的數據都可以直接存入,包括 JavaScript 對象。對象倉庫中,數據以"鍵值對"的形式保存,每一個數據記錄都有對應的主鍵,主鍵是獨一無二的,不能有重復,否則會拋出一個錯誤。
(2)異步。
IndexedDB 操作時不會鎖死瀏覽器,用戶依然可以進行其他操作,這與 LocalStorage 形成對比,后者的操作是同步的。異步設計是為了防止大量數據的讀寫,拖慢網頁的表現。
(3)支持事務。
IndexedDB 支持事務(transaction),這意味着一系列操作步驟之中,只要有一步失敗,整個事務就都取消,數據庫回滾到事務發生之前的狀態,不存在只改寫一部分數據的情況。
(4)同源限制
IndexedDB 受到同源限制,每一個數據庫對應創建它的域名。網頁只能訪問自身域名下的數據庫,而不能訪問跨域的數據庫。
(5)儲存空間大
IndexedDB 的儲存空間比 LocalStorage 大得多,一般來說不少於 250MB,甚至沒有上限。
(6)支持二進制儲存。
IndexedDB 不僅可以儲存字符串,還可以儲存二進制數據(ArrayBuffer 對象和 Blob 對象)。
緩存強制刷新
在強制刷新的時候瀏覽器就不在發送IF-Modified-Since
了,而會帶上
from disk cache & from memory cache
可以驗證請求是否使用了瀏覽器緩存和是否發送請求給服務器端。
當點擊鏈接、引入外部資源和瀏覽器的前進后退的時候。
from memory cache
字面理解是從內存中,其實也是字面的含義,這個資源是直接從內存中拿到的,不會請求服務器一般已經加載過該資源且緩存在了內存當中,當關閉該頁面時,此資源就被內存釋放掉了,再次重新打開相同頁面時不會出現from memory cache的情況
from disk cache
是從磁盤當中取出的,也是在已經在之前的某個時間加載過該資源,不會請求服務器但是此資源不會隨着該頁面的關閉而釋放掉,因為是存在硬盤當中的,下次打開仍會from disk cache
不做深入研究
js腳本,css,圖片,音視頻,字體
Age
是CDN添加的屬性表示在CDN中緩存了多少秒
via
用來標識CDN緩存經歷了哪些服務器,緩存是否命中,使用的協議
瀏覽器緩存原則
- 首頁可以看做是框架 應該禁用緩存,以保證加載的資源都是最新的
- 還有一些場景下我們希望禁用瀏覽器緩存。比如輪訓api上報數據數據
- 瀏覽器緩存很難徹底禁用,大家的做法是加版本號,隨機數等方法。
- 只緩存200響應頭的數據,像3XX這類跳轉的頁面不需要緩存。
- 對於js,css這類可以緩存很久的數據,可以通過加版本號的方式更新內容
- 不需要強一致性的數據,可以緩存幾秒
- 異步加載的接口數據,可以使用ETag來校驗。
- 在服務器添加Server頭,有利於排查錯誤
應用緩存思路
分為手機APP和Client以及是否遵循http協議,在沒有聯網的狀態下可以展示數據,流量消耗過多
- 漂亮的加載過程
- 提前下發避免秒殺時同時下發數據造成流量短時間暴增
- 兜底數據 在服務器崩潰和網絡不可用的時候展示
- 臨時緩存 退出即清理
- 固定緩存 展示框架這種,可能很長時間不會更新,可用隨客戶端下發
- 父子連 頁面跳轉時有一部分內容不需要重新加載,可用從父菜單帶過來
- 預加載 某些邏輯可用判定用戶接下來的操作,那么可用異步加載那些資源
- 異步加載 先展示框架,然后異步加載內容,避免主線程阻塞