Ã¥ÂÂè¨Â
说å°åÂÂ端çÂÂä¼ÂÃ¥ÂÂæÂÂ段ï¼Âå¿ ç¶æ¯离ä¸Âå¼Âç¼ÂÃ¥ÂÂãÂÂä½ æÂÂ没æÂÂÃ¥ÂÂç°å½ÂæÂÂ们访é®æÂÂç½Â页åÂÂéÂÂæÂÂè åÂÂè¿ÂçÂÂæ¶åÂÂï¼Âå 载éÂÂ度å¾Âå¿«ï¼Âä½ÂéªÂæÂÂ好çÂÂæÂÂè§Âï¼Âè¿Âå°±æ¯ç¼ÂÃ¥ÂÂ带æÂ¥çÂÂ好å¤ÂãÂÂ
为ä»Âä¹Âè¦Âç¨ç¼ÂÃ¥ÂÂ
ä¸ÂèÂŽÂÂ对çÂÂæ¯éÂÂæÂÂèµÂæºÂå¦ÂCSSãÂÂJSãÂÂå¾çÂÂçÂÂï¼ÂÃ¥ÂÂå å¦Âä¸Âï¼Â
- 请æ±Âæ´快
- èÂÂçÂÂ带宽
- éÂÂä½ÂæÂÂå¡å¨åÂÂÃ¥ÂÂ
æµÂè§Âå¨çÂÂç¼ÂÃ¥ÂÂçÂÂçÂÂ¥
ç®çÂÂï¼ÂÃ¥ÂÂå°Â页é¢éÂÂå¤ÂçÂÂhttp请æ±Â
ç¼ÂÃ¥ÂÂç±»åÂÂ
1. 强ç¼ÂÃ¥ÂÂ
- å®Âä¹Âï¼Â请æ±ÂçÂÂèµÂæºÂæΡç¼ÂÃ¥ÂÂä¸ÂæÂÂ,èµÂæºÂä»ÂæΡç¼ÂÃ¥ÂÂè·åÂÂ,ä¸ÂéÂÂè¦ÂÃ¥ÂÂ起请æ±ÂãÂÂ
- ç¨æ³Â: Ã¥ÂÂ端éÂÂè¿Â设置åÂÂåºÂ头ä¸ÂçÂÂ
Cache-Control:max-age=xxx
æÂÂèÂÂexpires:xxx
(xxx代表æÂÂ个æ¶é´) 两ç§Âæ¹å¼ÂæÂ¥æ§å¶èµÂæºÂ被æµÂè§Âå¨ç¼ÂÃ¥ÂÂçÂÂæ¶é¿ãÂÂä½Âæ¯è¿Âç§Â强ç¼ÂÃ¥ÂÂä¼Âåºç°åÂÂ端修æ¹äºÂèµÂæºÂèÂÂÃ¥ÂÂ端没æÂÂ对åºÂä¿®æ¹ï¼Âä¸Âä¸Âè´çÂÂç°象ãÂÂ
强å¶å·æ°页é¢ å éÂÂè¿ÂæµÂè§Âå¨urlå°åÂÂæ Â访é®çÂÂèµÂæºÂï¼ è¿Â两ç§Âæ åÂ悯Â认ä¼Âå¨请æ±Â头ä¸Â设置 Cache-Control:no-cacheï¼ÂæÂÂ该å±Âæ§æµÂè§Âå¨ä¼Â忽çÂ¥åÂÂåºÂ头ä¸Âç Cache-ControlãÂÂ
Cache-Control
Ã¥ÂÂexpires
åºå«ï¼ÂCache-Control
设置çÂÂæ¯维æÂÂçÂÂæ¶é¿ï¼ÂèÂÂexpires
设置çÂÂæ¯æªæ¢çÂÂæ¶é´ãÂÂå ¶ä¸Âexpires
æÂÂæ¶é´差é®é¢Âï¼Â误差ç¸æ¯ÂCache-Control
è¾Â大ãÂÂ
ä¸Âé¢æÂÂ们å¨Nodeç¯å¢Âä¸ÂçÂÂ个demo,æÂÂ个html页é¢ï¼Âå ¶ä¸Âæ¾äºÂä¸Â段æÂÂÃ¥ÂÂÃ¥ÂÂç §çÂÂï¼ÂæÂ¥ä¸ÂæÂ¥æÂÂ们å¨jsæÂÂ件ä¸Âå¼Âå¯serveræÂÂå¡ãÂÂ
const http=require('http');
const path = require('path');
const fs=require('fs');
const mime=require('mime') //æÂ¥æ¶ä¸Â个æÂÂ件åÂÂç¼Âï¼Âè¿ÂÃ¥ÂÂä¸Â个æÂÂ件类åÂÂ
const server=http.createServer((req,res)=>{
const filePath=path.resolve(__dirname,`www/${req.url}`);
if(fs.existsSync(filePath)){//è·¯å¾Âæ¯å¦åÂÂæ³Â
const stats=fs.statSync(filePath) //è·åÂÂæÂÂ件信æ¯
const isDir=stats.isDirectory() //å¤æÂÂæ¯å¦æ¯æÂÂ件夹
if(isDir){
filePath=path.join(filePath,'index.html')//æ¯æÂÂ件夹çÂÂè¯ÂæÂÂå¨å¨路å¾ÂÃ¥ÂÂé¢添å index.html
}
if(!isDir||fs.existsSync(filePath)){
const content=fs.readFileSync(filePath)//è·åÂÂ该路å¾Âä¸ÂÃ¥ÂÂ
容
//Ã¥ÂÂ端请æ±ÂçÂÂè·¯å¾ÂçÂÂÃ¥ÂÂç¼ÂÃ¥ÂÂ
const {ext}=path.parse(filePath)
//å©ç¨mimeæÂÂ件ï¼Âå¨æÂÂ设置ç¼Âç Âç±»åÂÂ
res.writeHead(200,{'Content-Type': `${mime.getType(ext)};charset=utf-8`})
const fileStream=fs.createReadStream(filePath) //å°ÂæÂÂ件读åÂÂæÂÂä¸Â个æµÂç±»åÂÂ
fileStream.pipe(res) //å°ÂæÂÂ件æµÂ导åÂ
¥å°resä¸Â
}
}
})
server.listen(3000,()=>{
console.log('listening on 3000');
})
æÂ¥ä¸ÂæÂ¥æÂÂ们å¼Âå¯强ç¼ÂÃ¥ÂÂï¼Âä¸Âé¢以Cache-Control
为ä¾Âï¼Âexpires
ä¹Âæ¯ä¸Âæ ·çÂÂÃ¥ÂÂçÂÂï¼Âå¯èªè¡ÂæµÂè¯ÂãÂÂå¨以ä¸Â代ç ÂçÂÂåºç¡Âä¸ÂåªéÂÂè¦Âå¨åÂÂåºÂ头éÂÂ设置Cache-Control
å³å¯ãÂÂ
//const time=new Date(Date.now()+36000000).toUTCString()//ç¨æ¥设置æªè³æ¶é´
let status = 200
res.writeHead(status, {
'Content-Type': `${mime.getType(ext)};charset=utf-8`,
'Cache-control': 'max-age=3600', // ç¼ÂÃ¥ÂÂä¸Âå°Âæ¶
//'expires':time //ç¼ÂÃ¥ÂÂ1hÃ¥ÂÂè¿ÂæÂÂ
});
å¨å·æ°页é¢2次åÂÂ(éÂÂ强å¶),å¯以åÂÂç°å¾çÂÂ被ç¼ÂÃ¥ÂÂä¸ÂæÂ¥ï¼Âè¿Âè¡Âæ¶é´0ï¼ÂèÂÂindex.htmlæÂÂ件çÂÂ大å°ÂÃ¥ÂÂè¿Âè¡Âæ¶é´ä¹ÂÃ¥ÂÂå°ÂäºÂï¼Âä¹Âé½å¯以å¨åÂÂåºÂ头ä¸ÂçÂÂå°Cache-Control
ãÂÂ
2. Ã¥ÂÂÃ¥ÂÂç¼ÂÃ¥ÂÂ
大å¤Âæ°æ åµä¸Âä» ä» åªæÂÂ强ç¼ÂÃ¥ÂÂæ¯ä¸Âå¤ÂçÂÂãÂÂæÂÂæ¶åÂÂæÂÂ们ä¼ÂéÂÂå°å¨åÂÂ端修æ¹äºÂæÂÂ个èµÂæºÂÃ¥ÂÂèÂÂÃ¥ÂÂ端çÂÂèµÂæºÂå´没æÂÂç¸åºÂçÂÂæ¹åÂÂï¼Âè¿Âå¾Âå¾Âä¸Âæ¯æÂÂ们å¸ÂæÂÂçÂÂç»ÂæÂÂï¼ÂæÂÂ以è¿Âæ¶åÂÂå°±è¦Âç¨å°åÂÂÃ¥ÂÂç¼ÂÃ¥ÂÂäºÂãÂÂæ¤ç¼ÂÃ¥ÂÂä¹ÂæÂÂ两个æ¹æ³ÂâÂÂâÂÂLast-Modified å EtagãÂÂ
该ç¼ÂÃ¥ÂÂç¨æ¥辠å©强ç¼ÂÃ¥ÂÂï¼Â让urlå°åÂÂæ Â请æ±ÂçÂÂèµÂæºÂä¹Âè½被ç¼ÂÃ¥ÂÂãÂÂ(解å³äºÂ强ç¼ÂÃ¥ÂÂÃ¥ÂÂå¨çÂÂ缺ç¹)
1. Ã¥ÂÂ端设置åÂÂåºÂ头ä¸Âç Last-Modified:xxx
Ã¥ÂÂå©请æ±Â头ä¸Âç if-modified-since
(第ä¸Â次请æ±Â没æÂÂæ¤åÂÂ段ï¼Â第äºÂ次以åÂÂæÂÂæÂÂ) æÂ¥æ¯Âè¾Âæ¯å¦åÂÂÃ¥ÂÂåºÂ头ä¸ÂLast-Modified
çÂÂå¼ç¸çÂÂ(å³å¤æÂÂÃ¥ÂÂ端ä¸ÂæÂÂÃ¥ÂÂä¿®æ¹çÂÂæ¶é´æ¯å¦åÂÂÃ¥ÂÂ端请æ±Â头éÂÂçÂÂä¸Âæ ·)ï¼Â以æ¤å¤æÂÂèµÂæºÂæÂÂ件æ¯å¦被修æ¹ï¼Âå¦ÂæÂÂ被修æ¹åÂÂè¿ÂÃ¥ÂÂæ°çÂÂèµÂæºÂï¼Âå¦åÂÂè¿ÂÃ¥ÂÂ304ç¶æÂÂç Âï¼Â让åÂÂ端读åÂÂæΡèµÂæºÂãÂÂ
const http = require('http');
const fs = require('fs');
const path = require('path');
const mime = require('mime');
const server = http.createServer((req, res) => {
let filePath = path.resolve(__dirname, `www/${req.url}`)
if (fs.existsSync(filePath)) {
const stats = fs.statSync(filePath)
if (stats.isDirectory()) { // Ã¥ÂÂ端请æ±ÂçÂÂæ¯ç®å½Â
filePath = path.join(filePath, 'index.html')
}
// ç¼ÂÃ¥ÂÂ
const { ext } = path.parse(filePath) // .html .png
const timeStamp = req.headers['if-modified-since']//第ä¸Â次请æ±Âæ¯ä¸ÂÃ¥ÂÂå¨çÂÂ
let status = 200
if (timeStamp && Number(timeStamp) === stats.mtimeMs) {//å¤æÂÂ请æ±Â头éÂÂçÂÂÃ¥ÂÂÃ¥ÂÂ端æÂÂÃ¥ÂÂä¿®æ¹æ¶é´æ¯å¦ç¸åÂÂ
status = 304
}
res.writeHead(status, {
'Content-Type': mime.getType(ext),
'Cache-Control': 'max-age=86400', // ç¼ÂÃ¥ÂÂä¸Â天
'Last-Modified': stats.mtimeMs //æÂÂ件æÂÂÃ¥ÂÂä¿®æ¹æ¶é´æ³
})
// 读åÂÂæÂÂ件并è¿ÂÃ¥ÂÂ
if (status === 200) {
const fileStream = fs.createReadStream(filePath) //å°ÂæÂÂ件读åÂÂæÂÂä¸Â个æµÂç±»åÂÂ
fileStream.pipe(res) //å°ÂæÂÂ件æµÂ导åÂ
¥å°resä¸Â
} else {
res.end()
}
} else {
res.writeHead(404, {'Content-Type': 'text/html'})
res.end('<h1>Not Found</h1>')
}
})
server.listen(3000, () => {
console.log('Server is running on port 3000');
})
2. Ã¥ÂÂ端设置åÂÂåºÂ头ä¸ÂçÂÂEtag
ï¼ æÂÂ件çÂÂç¾åÂÂ
- 请æ±Â头ä¸Âä¼Â被æº带
If-None-Match
(第ä¸Â次请æ±Â没æÂÂæ¤åÂÂ段ï¼Â第äºÂ次以åÂÂæÂÂæÂÂ)ã - æ ¹æ®æÂÂ件éÂÂé¢çÂÂå 容æ¯å¦被修æ¹æÂ¥å¤æÂÂãÂÂÃ¥ÂÂ端ä¼Âæ¯Â对è¿Â个åÂÂ端åÂÂéÂÂè¿ÂæÂ¥çÂÂEtagæ¯å¦ä¸ÂÃ¥ÂÂ端çÂÂç¸å ï¼Âå¦ÂæÂÂç¸åÂÂï¼Âå°±å°ÂIf-None-MatchçÂÂå¼设为falseï¼Âè¿ÂÃ¥ÂÂç¶æÂÂ为304ï¼ åÂÂ端继ç»Â使ç¨æΡç¼ÂÃ¥ÂÂï¼Âå¦ÂæÂÂä¸Âç¸åÂÂï¼Âå°±å°ÂIf-None-MatchçÂÂå¼设为trueï¼Âè¿ÂÃ¥ÂÂç¶æÂÂ为200ï¼ÂÃ¥ÂÂ端éÂÂæ°解æÂÂÃ¥ÂÂ端è¿ÂÃ¥ÂÂçÂÂæ°æ®ãÂÂ
const http = require('http');
const path = require('path');
const fs = require('fs');
const mime = require('mime'); // æÂ¥æ¶ä¸Â个æÂÂ件åÂÂç¼Âï¼Âè¿ÂÃ¥ÂÂä¸Â个æÂÂ件类åÂÂ
const md5 = require('crypto-js/md5')
const server = http.createServer((req, res) => {
let filePath = path.resolve(__dirname, `www/${req.url}`) //http://localhost:3000/index.html
if (fs.existsSync(filePath)) { // è·¯å¾Âæ¯å¦åÂÂæ³Â
const stats = fs.statSync(filePath); // è·åÂÂæÂÂ件信æ¯
const isDir = stats.isDirectory(); // å¤æÂÂæ¯å¦æ¯æÂÂ件夹
if (isDir) {
filePath = path.join(filePath, 'index.html');
}
// 读åÂÂæÂÂ件
if (!isDir || fs.existsSync(filePath)) {
// Ã¥ÂÂ端请æ±ÂçÂÂè·¯å¾ÂçÂÂÃ¥ÂÂç¼ÂÃ¥ÂÂ
const { ext } = path.parse(filePath);
const content = fs.readFileSync(filePath);
let status = 200
// å¤æÂÂæÂÂ件æ¯å¦修æ¹è¿Â
if (req.headers['if-none-match']==md5(content)) {
status = 304
}
res.writeHead(status, {
'Content-Type': `${mime.getType(ext)};charset=utf-8`,
'Cache-control': 'max-age=3600', // ç¼ÂÃ¥ÂÂä¸Âå°Âæ¶
'Etag': md5(content) // æÂÂ件èµÂæºÂçÂÂmd5å¼
});
if (status === 200) {
const fileStream = fs.createReadStream(filePath)
fileStream.pipe(res);
} else {
res.end()
}
}
}
})
server.listen(3000, () => {
console.log('Server is running on port 3000');
})
Last-Modified VS Etag
Last-Modified
æ¯根æ®æÂÂ件çÂÂä¿®æ¹æ¶é´æÂ¥å¤å®ÂæÂÂ件æ¯å¦被修æ¹è¿Âï¼ÂèÂÂÃ¥ÂÂ设æÂÂ件被修æ¹åÂÂÃ¥ÂÂÃ¥ÂÂ次被æ¹åÂÂÃ¥ÂÂç¶æÂÂï¼Âç³»ç»Âä¾Âæ§ä¼Â认å®ÂæÂÂ件æ¯被修æ¹è¿ÂçÂÂï¼Âä»ÂèÂÂ导è´åÂÂ端çÂÂç¼ÂÃ¥ÂÂ失败ãÂÂEtag
æ¯根æ®æÂÂ件å 容å¶ä½ÂçÂÂç¾åÂÂï¼Âä¸Âä¼Â带æ¥以ä¸Âé®é¢ÂãÂÂ
ç»ÂæÂÂè¯Â
æ¬ç¯ÂæÂÂç« å°±å°æ¤为æ¢å¦ï¼Âç±äºÂæ¬人ç»ÂéªÂæ°´å¹³æÂÂéÂÂï¼Âé¾å Âä¼ÂæÂÂ纰æ¼Âï¼Â对æ¤欢è¿ÂæÂÂæ£ãÂÂå¦Âè§Âå¾Âæ¾ÂÂ对你æÂÂ帮å©çÂÂè¯Âï¼Â欢è¿Âç¹èµÂæ¶èÂÂâ¤â¤â¤ï¼Âæ¨çÂÂç¹èµÂæ¯æÂÂç»ÂÃ¥ÂÂä½ÂçÂÂå¨åÂÂï¼ÂæÂÂè°¢æ¯æÂÂãÂÂè¦Âæ¯æ¨è§Âå¾ÂæÂÂæ´好çÂÂæ¹æ³Âï¼Â欢è¿Âè¯Â论ï¼ÂæÂÂåº建议ï¼Â