本文所有程式碼皆採用express搭建的簡單伺服器
web快取描述 :
Web 快取是可以自動儲存常見文件副本的 HTTP 裝置。當 Web 請求抵達快取時, 如果本地有“已快取的”副本,就可以從本地儲存裝置而不是原始伺服器中提取這 個文件。(此結論來自http權威指南)
快取的優缺點:
優點:
- 快取減少了冗餘的資料傳輸,節省了你的網路費用。
- 快取緩解了網路瓶頸的問題。不需要更多的頻寬就能夠更快地載入頁面。
- 快取降低了對原始伺服器的要求。伺服器可以更快地響應,避免過載的出現。
- 快取降低了距離時延,因為從較遠的地方載入頁面會更慢一些。
缺點:
- 快取中的資料可能與伺服器的資料不一致;
- 消耗記憶體;
快取驗證概述:
快取可分為強快取和協商快取。
1,瀏覽器進行資源請求時,會判斷response headers是否命中強快取,如果命中,直接從本地讀取快取,不會向伺服器傳送請求,
2,當強快取沒有命中時,會傳送請求到服務端,判斷協商快取是否命中,如果命中,伺服器將請求返回,不會返回資源,告訴瀏覽器從本地讀取快取。如何不命中,伺服器直接返回資源
區別: 強快取命中,不會請求伺服器,直接請求快取;協商快取命中,會請求伺服器,不會返回內容,然後讀取快取;
from memory cache 和 from disk cache的區別
from memory cache:字面理解是從記憶體中,其實也是字面的含義,這個資源是直接從記憶體中拿到的,不會請求伺服器一般已經載入過該資源且快取在了記憶體當中,當關閉該頁面時,此資源就被記憶體釋放掉了,再次重新開啟相同頁面時不會出現from memory cache的情況
from disk cache:同上類似,此資源是從磁碟當中取出的,也是在已經在之前的某個時間載入過該資源,不會請求伺服器但是此資源不會隨著該頁面的關閉而釋放掉,因為是存在硬碟當中的,下次開啟仍會from disk cache (來自:blog.csdn.net/garrettzxd/… )
以下是快取實現的四種方式
強快取
強快取又分為Expires 和 Cache-Control
Expires,該值是一個GMT時間格式個字串,瀏覽器進行第一次請求時,伺服器會在返回頭部加上Expires,下次請求,如果在這個時間之前則命中快取,
app.get('/', (req, res) => {
const cssContent = path.join(__dirname, './html/index.html');
fs.readFile(cssContent, function(err, data) {
res.setHeader("Expires", new Date(Date.now() + 2592000000).toUTCString());
res.end(data);
})
});
複製程式碼
Cache-Control ,該值是利用max-age判斷快取的生命週期,是以秒為單位,如何在生命週期時間內,則命中快取
app.get('/', (req, res) => {
const cssContent = path.join(__dirname, './html/index.html');
fs.readFile(cssContent, function(err, data) {
res.setHeader("Cache-Control", "max-age=0");
res.end(data);
})
});
複製程式碼
命中快取:
協商快取
協商快取利用Last-Modified , If-Modified-Since 和 ETag , If-None-Match來實現
Last-Modified , If-Modified-Since
Last-Modified: 表示為為實體頭部部分,response返回,表示為資源的最後更新時間
If-Modified-Since:通過比較兩次的時間判斷,資源在請求期間是否有修改,假如沒有修改,則命中協商快取,瀏覽器從快取中讀取資源,如果沒有命中,資源有過修改,返回新的Last-Modified時間和伺服器資源
app.get('/', (req, res) => {
const cssContent = path.join(__dirname, './html/index.html')
fs.stat(cssContent, (err, start) => {
if (req.headers['if-modified-since'] === start.mtime.toUTCString()) {
res.writeHead(304, 'Not Modified');
res.end();
} else {
fs.readFile(cssContent, function (err, data) {
let lastModified = start.mtime.toUTCString();
res.setHeader('Last-Modified', lastModified);
res.writeHead(200, 'OK');
res.end(data);
})
}
})
});
複製程式碼
ETag , If-None-Match
有些情況下僅判斷最後修改日期來驗證資源是否有改動是不夠的:
1,存在週期性重寫某些資源,但資源實際包含的內容並無變化;
2,被修改的資訊並不重要,如註釋等;
3,Last-Modified無法精確到毫秒,但有些資源更新頻率有時會小於一秒。
ETag:為相應頭部欄位,表示資源內容的唯一標識,隨伺服器response返回;
If-None-Match: 伺服器比較請求頭中的If-None-Match和當前資源中的etag是否一致,來判斷資源是否修改過,如果沒有修改,則命中快取,瀏覽器從快取中讀取資源,如果修改過,伺服器會返回新的etag,並返回資源;
app.get('/home', (req, res) => {
const cssContent = path.join(__dirname, './html/index.html')
fs.stat(cssContent, (err, start) => {
let etag = md5(cssContent);
if (req.headers['if-none-match'] === etag) {
res.writeHead(304, 'Not Modified');
res.end();
} else {
fs.readFile(cssContent, function (err, data) {
res.setHeader('Etag', etag);
res.writeHead(200, 'OK');
res.end(data);
})
}
})
});
複製程式碼
不推薦使用 Expires 首部,它指定的是實際的過期日期而不是秒數。HTTP 設計者 後來認為,由於很多伺服器的時鐘都不同步,或者不正確,所以最好還是用剩餘秒 數,而不是絕對時間來表示過期時間。
ETag解決了Last-Modified使用時可能出現的資源的時間戳變了但內容沒變及如果再一秒鐘以內資源變化但Last-Modified沒變的問題,感覺ETag更加穩妥。
補充:根據瀏覽器快取策略,Expire和Cache-Control用回車、後退、F5重新整理會跳過本地快取,每次都會從伺服器中獲資料。