占位占位
時間:2016-12-12 23:01:08
學(xué)習(xí)了理論的知識澎迎,還是需要實踐來加深對HTTP緩存的理解。 【這里占個位置球凰,告訴自己要去實踐一下】
更新時間:2016-12-14 15:04:34
一凛膏、教程
【推薦】根據(jù)教程一步一步敲出來,大大加深了對緩存的理解熊经,也知道實際中該如何使用泽艘。
二、采用技術(shù)以及緩存應(yīng)用學(xué)習(xí)
采用NodeJs的 http镐依,url匹涮,fs,path馋吗,zlib 實現(xiàn)一個簡單的web服務(wù)器焕盟,支持強(qiáng)緩存,協(xié)商緩存,GZip壓縮脚翘。
1. 強(qiáng)緩存 灼卢, 協(xié)商緩存
強(qiáng)緩存主要是通過設(shè)置 Response 的
Expires 和 Cache-Control`協(xié)商緩存主要是設(shè)置Response 返回
Last-Modified
, 瀏覽器請求資源的時候来农,會把這個值放在If-Modified-Since
的Header里面鞋真,然后這里判斷是否過時了。協(xié)商緩存還有一個ETag可以配置使用沃于,但是ETag需要這里生成唯一的資源標(biāo)識涩咖,然后返回給瀏覽器,瀏覽器再次請求一個資源的時候繁莹,會帶上這個ETag標(biāo)記檩互,放在 Request的
If-None-Match
的 header字段,如果文件沒有被修改咨演,則返回304 闸昨,瀏覽器采用本地緩存。
2. 既生Last-Modified何生Etag薄风?
你可能會覺得使用Last-Modified已經(jīng)足以讓瀏覽器知道本地的緩存副本是否足夠新饵较,為什么還需要Etag(實體標(biāo)識)呢?HTTP1.1中Etag的出現(xiàn)主要是為了解決幾個Last-Modified比較難解決的問題:
Last-Modified標(biāo)注的最后修改只能精確到秒級遭赂,如果某些文件在1秒鐘以內(nèi)循诉,被修改多次的話,它將不能準(zhǔn)確標(biāo)注文件的修改時間
如果某些文件會被定期生成撇他,當(dāng)有時內(nèi)容并沒有任何變化茄猫,但Last-Modified卻改變了,導(dǎo)致文件沒法使用緩存
有可能存在服務(wù)器沒有準(zhǔn)確獲取文件修改時間逆粹,或者與代理服務(wù)器時間不一致等情形
Etag是服務(wù)器自動生成或者由開發(fā)者生成的對應(yīng)資源在服務(wù)器端的唯一標(biāo)識符募疮,能夠更加準(zhǔn)確的控制緩存。Last-Modified與ETag是可以一起使用的僻弹,服務(wù)器會優(yōu)先驗證ETag阿浓,一致的情況下,才會繼續(xù)比對Last-Modified蹋绽,最后才決定是否返回304芭毙。
3. 瀏覽器行為
- 在按
CMD+R
刷新瀏覽器的時候,一直沒有看到使用緩存卸耘,莫名其妙的退敦,后面發(fā)現(xiàn)是 按刷新的時候,跳過了強(qiáng)緩存蚣抗,協(xié)商緩存會驗證侈百。 -
CTRL+CMD+R
跳過 強(qiáng)緩存 和 協(xié)商緩存 - 只有瀏覽器地址欄,輸入地址,或者頁面超鏈接跳轉(zhuǎn)钝域,采用使用強(qiáng)緩存驗證
三讽坏、代碼
根據(jù)教程一步一步自己敲出來更好。
//config.js
exports.Expires = {
fileMatch: /^(gif|png|jpg|js|css)$/ig,
maxAge: 60 * 60 * 24 * 365
}
exports.Compress = {
match: /css|js|html/ig
}
//MIME.js
// Header 的 Content-Type
exports.types = {
"css": "text/css",
"gif": "image/gif",
"html": "text/html",
"ico": "image/x-icon",
"jpeg": "image/jpeg",
"jpg": "image/jpeg",
"js": "text/javascript",
"json": "application/json",
"pdf": "application/pdf",
"png": "image/png",
"svg": "image/svg+xml",
"swf": "application/x-shockwave-flash",
"tiff": "image/tiff",
"txt": "text/plain",
"wav": "audio/x-wav",
"wma": "audio/x-ms-wma",
"wmv": "video/x-ms-wmv",
"xml": "text/xml"
};
//index.js
var PORT = 9999
var http = require('http')
var url = require('url')
var fs = require('fs')
var path = require('path')
var zlib = require("zlib")
var MIME = require('./MIME.js').types
var config = require('./config.js')
var server = http.createServer(function (request, response) {
// 獲取資源路徑
var pathname = url.parse(request.url).pathname
var realPath = 'assets' + pathname
fs.exists(realPath, function (exists) {
if (!exists) {
response.writeHead(404, { 'Content-Type': 'text/plain' })
response.write('This Request Url ' + pathname + ' was not found on this server')
response.end()
} else {
fs.readFile(realPath, 'binary', function (err, file) {
if (err) {
response.writeHead(500, { 'Content-Type': 'text/plain' })
response.end(err.Error)
} else {
var ext = path.extname(realPath)
ext = ext ? ext.slice(1) : 'unknown'
var contentType = MIME[ext] || 'text/plain'
//設(shè)置強(qiáng)緩存 Expires Cache-Control
var expires = new Date()
expires.setTime(expires.getTime() + config.Expires.maxAge * 1000)
response.setHeader('Expires', expires.toUTCString())
response.setHeader('Cache-Control', 'max-age=' + config.Expires.maxAge)
//設(shè)置協(xié)商緩存 Last-Modified If-Modified-Since Tag If-None-Match
fs.stat(realPath, function (err, stat) {
var lastModified = stat.mtime.toUTCString()
response.setHeader("Last-Modified", lastModified)
if (request.headers["If-Modified-Since"] && lastModified == request.headers["If-Modified-Since"]) {
response.writeHead(304, "Not Modified")
response.end()
} else {
//使用GZIP壓縮資源 利用Node的 zlib 壓縮
var raw = fs.createReadStream(realPath)
var acceptEncoding = request.headers['accept-encoding'] || ""
var matched = ext.match(config.Compress.match)
if (matched && acceptEncoding.match(/\bgzip\b/)) {
response.writeHead(200, "Ok", { 'Content-Encoding': 'gzip' });
raw.pipe(zlib.createGzip()).pipe(response);
} else if (matched && acceptEncoding.match(/\bdeflate\b/)) {
response.writeHead(200, "Ok", { 'Content-Encoding': 'deflate' });
raw.pipe(zlib.createDeflate()).pipe(response);
} else {
response.writeHead(200, { 'Content-Type': contentType })
raw.pipe(response);
}
}
})
console.log('load assets file:' + realPath)
}
})
}
})
})
server.listen(PORT)
console.log('Server runing at port:' + PORT + '...')