瀏覽器緩存
1. 前言
瀏覽器緩存 是瀏覽器將用戶請求過的靜態資源(html、css、js),存儲到電腦本地磁盤中,當瀏覽器再次訪問時,就可以直接從本地加載了,不需要再去服務端請求了。
但也不是說緩存沒有缺點,如果處理不當,可能會導致服務端代碼更新了,但是用戶卻還是老頁面。所以前端們要針對項目中各個資源的實際情況,做出合理的緩存策略。
緩存的優點:
- 減少了冗余的數據傳輸,節省網費
- 減少服務器的負擔,提升網站性能
- 加快了客戶端加載網頁的速度
2. 緩存流程
這里先介紹一下瀏覽器緩存資源的一個大概的流程。
我們可以認為,瀏覽器里有一個專門存放緩存規則的一個數據庫,也可以說是一個映射表,把緩存資源信息,同電腦磁盤中的實際文件的地址,對應起來。(大概意思,別較真)
而這個緩存規則的表,在瀏覽器中是可以看到的: chrome://cache/
不過我升級了瀏覽器之后,就不好使了,但是找到了 chrome://net-internals/#httpCache ,不知道是不是就是原來的,知道的同學也可以反饋一下
瀏覽器第一次請求資源時
上面所說的 緩存規則,就是聲明所請求的這個資源,要采取哪種緩存策略?緩存多長時間?等等。。。而這個規則,是在http的header中的返回來的。
注意: 是response header ,而不是 request header !!!
而實際上, request header 中也會攜帶規則信息,下面會講,要區分 request 和 response
3. 緩存規則
強緩存和協商緩存。
強緩存
簡單粗暴,如果資源沒過期,就取緩存,如果過期了,則請求服務器。
如何判斷資源是否過期呢,也就是說強緩存的規則怎么看?
主要是看 response headers 中的 Cache-Control 的值,圖中的max-age = 31xxxxxxx,就是說在這些秒內,都直接使用緩存,超過了就繼續請求服務器
而和 Cache-Control 並列的,還有一個 Expires ,已經基本淘汰了,所以不用管
Cache-Control 的幾個取值含義:
private: 僅瀏覽器可以緩存
public: 瀏覽器和代理服務器都可以緩存(對於private和public,前端可以認為一樣,不用深究)
max-age=xxx 過期時間(重要)
no-cache 不進行強緩存(重要)
no-store 不強緩存,也不協商緩存,基本不用,緩存越多才越好呢
注意:規則可以同時多個
所以,對於強緩存,我們主要研究 Cache-Control 中的 max-age 和 no-cache
所以,判斷該資源是否命中強緩存,就看 response 中 Cache-Control 的值,如果有max-age=xxx秒,則命中強緩存。如果Cache-Control的值是no-cache,說明沒命中強緩存,走協商緩存。
強緩存流程:
所以強緩存步驟已經很清晰了:
- 第一次請求 a.js ,緩存表中沒該信息,直接請求后端服務器。
- 后端服務器返回了 a.js ,且 http response header 中 cache-control 為 max-age=xxxx,所以是強緩存規則,存入緩存表中。
- 第二次請求 a.js ,緩存表中是 max-age, 那么命中強緩存,然后判斷是否過期,如果沒過期,直接讀緩存的a.js,如果過期了,則執行協商緩存的步驟了。
注意
這里有個問題,就是 max-age = 0 ,和 no-cache 有啥區別,我理解的是,no-cache直接不進行強緩存,讓你去走協商緩存,而max-age=0是進行強緩存,但是過期了,需要更新。。。雖然實際上看起來兩者效果是一樣的。
www.itranslater.com/qa/details/…
協商緩存
觸發條件:
- Cache-Control 的值為 no-cache (不強緩存)
- 或者 max-age 過期了 (強緩存,但總有過期的時候)
也就是說,不管怎樣,都可能最后要進行協商緩存(no-store除外)
這個圖,雖然強緩存命中,但是也有 ETag 和 Last-Modified ,這兩個就是協商緩存的相關規則。雖然之前的強緩存流程和他倆沒關。。。
ETag:每個文件有一個,改動文件了就變了,可以看似md5
Last-Modified:文件的修改時間
也就是說,每次http返回來 response header 中的 ETag和 Last-Modified,在下次請求時在 request header 就把這兩個帶上(但是名字變了ETag-->If-None-Match,Last-Modified-->If-Modified-Since ),服務端把你帶過來的標識,資源目前的標識,進行對比,然后判斷資源是否更改了。
這個過程是循環往復的,即緩存表在每次請求成功后都會更新規則。
1. 第n次請求成功時:
2. 緩存表中更新該資源的 ETag 值
3. 第n+1次請求:
從緩存表中取該資源最新的ETag,然后加在 request header 中, 注意變名字了,由 ETag -- > If-None-Match
圖:
所以協商緩存步驟總結:
- 請求資源時,把用戶本地該資源的 ETag 同時帶到服務端,服務端和最新資源做對比。
- 如果資源沒更改,返回304,瀏覽器讀取本地緩存。
- 如果資源有更改,返回200,返回最新的資源。
4. 緩存命中顯示
- 從服務器獲取新的資源
- 命中強緩存,且資源沒過期,直接讀取本地緩存
- 命中協商緩存,且資源未更改,讀取本地緩存
注意:協商緩存無論如果,都要向服務端發請求的,只不過,資源未更改時,返回的只是header信息,所以size很小;而資源有更改時,還要返回body數據,所以size會大。
7. 其他
0. 怎么配置資源的緩存規則
可以有后端服務器配置,也可以在nginx中配置,稍后會更新一張nginx的配置
1. 為什么要有Etag
你可能會覺得使用Last-Modified已經足以讓瀏覽器知道本地的緩存副本是否足夠新,為什么還需要Etag呢?HTTP1.1中Etag的出現(也就是說,ETag是新增的,為了解決之前只有If-Modified的缺點)主要是為了解決幾個Last-Modified比較難解決的問題:
-
一些文件也許會周期性的更改,但是他的內容並不改變(僅僅改變的修改時間),這個時候我們並不希望客戶端認為這個文件被修改了,而重新GET;
-
某些文件修改非常頻繁,比如在秒以下的時間內進行修改,(比方說1s內修改了N次),If-Modified-Since能檢查到的粒度是s級的,這種修改無法判斷(或者說UNIX記錄MTIME只能精確到秒);
-
某些服務器不能精確的得到文件的最后修改時間。
2. 強緩存與協商緩存的區別可以用下表來表示:
3. 用戶行為對緩存的影響
即:F5 會 跳過強緩存規則,直接走協商緩存;;;Ctrl+F5 ,跳過所有緩存規則,和第一次請求一樣,重新獲取資源
4. 項目緩存策略
比如 vue 項目,腳手架已經將更改的文件做 hash 處理了,因此一般的 js、css 文件不需要我們再去操作。
而對於 index.html,我們需要在 nginx 上做 no-store 處理,即完全不緩存 index.html,每次都請求最新的html。。。因為 html 中會外鏈 css、js,如果我 html 還是走的緩存,那鏈接的還是老的 css 啊,想想???
6. 總結
借兩個圖