不少時候,當打開瀏覽器的開發者工具,查看網絡請求,對於資源大小(Size)選項,除了有具體的數字大小,還有from memory cache、from disk cache字段之類出現。javascript
這裏就有不少疑問,這些字段表明着什麼意思?這些字段又是誰來決定的?前端
從字面意思理解,大概也能猜到,這些字段表明着緩存位置。 按優先級,Size選項字段可分爲:java
本質是做爲服務器與客戶端之間的代理服務器,伴隨着PWA出現。Service Worker真正意義上將緩存控制權交給了前端,相比於LocalStorage、SessionStorage,後二者只是單純的接口數據緩存,例如用戶信息(一個對象)、列表信息(一個數組),而前者能夠緩存靜態資源,甚至攔截網絡請求,根據網絡情況做出不一樣的緩存策略。固然,這不是本文討論的重點。後端
顧名思義,這個是將資源緩存在了內存中。事實上,全部的網絡請求都會被瀏覽器緩存到內存中,固然,內存容量有限,緩存不能無限存放在內存中,所以,註定是個短時間緩存。數組
內存緩存的控制權在瀏覽器,先後端都不能干涉。瀏覽器
與內存緩存相對的,這個是將資源緩存在硬盤中。雖然相比於內存,硬盤的讀取速度要慢不少,但總比沒有強。緩存
硬盤緩存的控制權在後端,經過什麼控制呢?經過HTTP響應頭控制,這是本文重點討論的。性能優化
disk cache也叫http cahce,由於其嚴格遵照http響應頭字段來判斷哪些資源是否要被緩存,哪些資源是否已通過期。絕大多數緩存都是disk cache。服務器
disk cahce分爲強制緩存與對比緩存。網絡
控制強制緩存的有兩種http響應頭字段:
Expires: Fri, 08 Feb 2019 05:37:33 GMT
字段的值就表明了資源的過時時間,不過這個值是相對於客戶端,而且客戶端本地時間能夠任意修改,所以這個字段並不可靠。Expires字段是Http 1.0的,Http 1.1 用Cache-Control字段替代它:
Cache-Control: max-age=2592000
Cache-control字段使用了絕對時間,單位爲秒,即最大有效時間,在有效時間內,客戶端直接從硬盤中讀取資源。
看個例子,用Node.js搭建一個靜態資源服務器,設置Cache-Control: max-age=2592000,每次請求都會被服務器打印出:
const server = http.createServer((req, res) => {
console.log(`收到請求,請求地址爲: ${req.url}`);
fs.readFile(path.resolve(__dirname, './image.png'), (err, file) => {
if (err) {
res.end(err.message);
}
res.setHeader('Cache-control', 'max-age=2592000');
res.end(file);
});
}).listen(3000);
console.log('localhost:3000服務已開啓!');
複製代碼
第一次訪問:
第二次訪問:
能夠看到,第一次請求,瀏覽器根據響應頭中的Cache-Control字段,將資源緩存在硬盤中,第二次請求,瀏覽器直接從硬盤中讀取資源,並無發送網絡請求到服務器。
Cache-Control字段有如下可取值:
不一樣於強制緩存,瀏覽器直接根據響應頭Cache-Control字段直接判斷緩存資源是否有效,對比緩存須要再次向服務器確認。
服務器經過響應頭Last-Modified告知瀏覽器,資源最後被修改的時間:
Last-Modified: Fri, 08 Feb 2019 15:20:04 GMT
當再次請求該資源時,瀏覽器須要再次向服務器確認,資源是否過時,其中的憑證就是請求頭If-Modified-Since字段,值爲上次請求中響應頭Last-Modified字段的值:
If-Modified-Since: Fri, 08 Feb 2019 15:20:04 GMT
服務器會接收If-Modified-Since字段的值與被請求資源的最後修改時間做比較
若是If-Modified-Since的值大於被請求資源的最後修改時間,則說明瀏覽器緩存的資源仍然有效,服務器會返回304狀態碼,告知瀏覽器直接取緩存便可。其中服務器返回的只有Http頭部,並不包含主體(否則就沒有緩存的意義了)。
不然,就跟正常的請求同樣,服務器返回200狀態碼,並附帶最新的資源。
看個例子,稍微修改下剛纔的Node.js代碼:
const server = http.createServer((req, res) => {
console.log(`收到請求,請求地址爲: ${req.url}`);
const filename = path.resolve(__dirname, './image.png');
fs.stat(filename, (err, stat) => {
const lastModified = stat.mtime.toUTCString();
if (lastModified === req.headers['if-modified-since']) {
res.writeHead(304, 'Not Modified');
res.end();
}
else {
fs.readFile(filename, (err, file) => {
if (err) {
res.end(err.message);
}
res.setHeader('Last-Modified', lastModified);
res.end(file);
});
}
});
}).listen(3000);
console.log('localhost:3000服務已開啓!');
複製代碼
第一次請求:
第二次請求:
比對兩次請求能夠看到,除了狀態碼變成了304,資源大小也從57.8K降到了90B,這也證實響應中不包含http主體。
Last-Modiflied與Expires同樣,也是有缺陷的。若是,資源的變化的時間間隔小於秒級,好比說是毫秒級的,或者說資源直接是動態生成的,那根據Last-Modified判斷,資源就是每時每刻都最新的,即被修改過!
因此,Etag & If-Node-Match 就是來解決這個問題的。
Etag字段的值爲文件的特殊標識,通常都是hash生成的,服務器存儲着資源的Etag值。接下來的流程都與Lst-Modified & If-Modified-Since一致,只不過,比較的值從最後修改時間變成了Etag值。
Etag的優勢在於,對於動態資源或者如今流行的Restful API返回的JSON數據,這些是沒有修改時間這一說法的,可是Http標準並無規定Etag值如何生成,所以咱們經過代碼本身生成Etag值。固然,計算Etag值會消耗服務器性能。
強制緩存與對比緩存是能夠同時存在的,而且強制緩存的優先級高於對比緩存。實際應用中,也是二者共同使用的。
看個例子,在響應頭中同時加上Cache-Control與Last-Modified:
res.setHeader('Cache-control', 'max-age=600');
res.setHeader('Last-Modified', lastModified);
複製代碼
第一次請求:
第二次請求:
能夠看到,雖然有Last-Modified字段,但仍是直接從硬盤中獲取資源。
Http緩存策略,其實只是前端緩存的一小部分,但零亂的知識點仍是很是多的。最終處理緩存仍是瀏覽器,各瀏覽器的處理方式可能有差別,實際應用中仍是要慎重考慮。
合理運用Http緩存,對前端性能優化仍是很是有幫助的!