圖 1 使用緩存的流程示意圖瀏覽器
下面從三個規則分別來說:緩存
請求頭部設置了If-Modified-Since,瀏覽器向服務器請求資源,服務端返回304狀態碼,瀏覽器則會使用本地文件。服務器
var handle = function (req, res) { fs.stat(filename, function (err, stat) { var lastModified = stat.mtime.toUTCString(); if (lastModified === req.headers['if-modified-since']) { res.writeHead(304, "Not Modified"); res.end(); } else { fs.readFile(filename, function(err, file) { var lastModified = stat.mtime.toUTCString(); res.setHeader("Last-Modified", lastModified); res.writeHead(200, "Ok"); res.end(file); }); } }); };
這裏採用時間戳的方式來實現,時間戳的方式有一些缺陷:spa
文件的時間戳改動了但內容不必定改動。code
時間戳只能精確到秒級別,更新頻繁的內容將沒法生效。blog
Etags(Entity Tag)能夠解決這個問題,由服務端生成,生成規則隨意。通常是根據文件內容生成散列值,那麼條件請求將不會受到時間戳改動形成帶寬的浪費。與If-Modified-Since/Last-Modified不一樣的是,Etags的請求相應是:If-None-Match/ETag資源
var getHash = function (str) {
var shasum = crypto.createHash('sha1'); return shasum.update(str).digest('base64'); }; var handle = function (req, res) { fs.readFile(filename, function(err, file) { var hash = getHash(file); var noneMatch = req.headers['if-none-match']; if (hash === noneMatch) { res.writeHead(304, "Not Modified"); res.end(); } else { res.setHeader("ETag", hash); res.writeHead(200, "Ok"); res.end(file); } }); };
儘管條件請求能夠在文件內容沒有改變的狀況下節省帶寬,可是仍是須要請求服務器,那麼能夠不請求服務器直接取本地文件嗎?字符串
Expires是一個GMT格式的時間字符串(GMT時間與北京時間相互能夠轉化),在服務器端設置Expires能夠告知瀏覽器要緩存的內容,只要本地還存在這個文件,在過時時間以前,都不會再發起請求。get
var handle = function (req, res) { fs.readFile(filename, function(err, file) { var expires = new Date(); expires.setTime(expires.getTime() + 10 * 365 * 24 * 60 * 60 * 1000); res.setHeader("Expires", expires.toUTCString()); res.writeHead(200, "Ok"); res.end(file); }); };
*缺陷:若是用戶本地時間和服務器時間不一致,那麼這個緩存機制就存在問題。hash
Cache-Control能夠避免這個問題,Cache-Control設置了一個max-age值,表示通過多長時間以後過時。(Cache-Control的優先級高於Expires)
var handle = function (req, res) { fs.readFile(filename, function(err, file) { res.setHeader("Cache-Control", "max-age=" + 10 * 365 * 24 * 60 * 60 * 1000); res.writeHead(200, "Ok"); res.end(file); }); };
咱們設置緩存能夠節省帶寬,可是也會帶來新的問題,若是文件內容發生變化,怎麼通知用戶去更新呢?