协商缓存(Conditional Requests)
协商缓存指的是浏览器每次请求时携带上次请求标识(如 Last-Modified
或 ETag
),服务器通过这些标识判断资源是否修改,如果没有修改,则返回 304 Not Modified
响应。
Last-Modified
和If-Modified-Since
:基于资源的最后修改时间进行缓存协商。ETag
和If-None-Match
:基于资源的内容哈希进行缓存协商。
适用场景:
- 动态内容:如用户的个人信息页面,这些页面数据可能会经常更新,可以使用协商缓存来减少不必要的传输。
- API 响应:特别是数据可能频繁变化的 API,可以使用协商缓存减少带宽消耗。
使用 Last-Modified
和 If-Modified-Since
const http = require('http');
const fs = require('fs');
const path = require('path');
const server = http.createServer((req, res) => {
const filePath = path.join(__dirname, 'static', 'file.txt'); // 示例文件路径
fs.stat(filePath, (err, stats) => {
if (err) {
res.statusCode = 404;
res.end('File not found');
return;
}
const lastModified = stats.mtime.toUTCString();
// 检查 If-Modified-Since 头
if (req.headers['if-modified-since'] === lastModified) {
res.statusCode = 304; // Not Modified
res.end();
return;
}
res.setHeader('Last-Modified', lastModified);
res.setHeader('Content-Type', 'text/plain');
const fileStream = fs.createReadStream(filePath);
fileStream.pipe(res);
});
});
server.listen(3000, () => {
console.log('Server is running on http://localhost:3000');
});
使用 ETag
和 If-None-Match
const http = require('http');
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
const server = http.createServer((req, res) => {
const filePath = path.join(__dirname, 'static', 'file.txt'); // 示例文件路径
fs.readFile(filePath, (err, data) => {
if (err) {
res.statusCode = 404;
res.end('File not found');
return;
}
const hash = crypto.createHash('md5').update(data).digest('hex');
const etag = `"${hash}"`;
// 检查 If-None-Match 头
if (req.headers['if-none-match'] === etag) {
res.statusCode = 304; // Not Modified
res.end();
return;
}
res.setHeader('ETag', etag);
res.setHeader('Content-Type', 'text/plain');
res.end(data);
});
});
server.listen(3000, () => {
console.log('Server is running on http://localhost:3000');
});
总结
Last-Modified
和If-Modified-Since
:基于资源的最后修改时间进行缓存协商。ETag
和If-None-Match
:基于资源的内容哈希进行缓存协商。
强制缓存(Cache-Control)
强制缓存指的是服务器在响应头中设置缓存控制策略,告知浏览器在一定时间内不需要向服务器请求,直接使用本地缓存。常见的 Cache-Control
头值包括:
max-age=<seconds>
:指定缓存的有效时间,单位是秒。public
:表示响应可以被任何缓存存储。private
:表示响应只能被用户的浏览器缓存,不能被共享缓存(如代理服务器)存储。no-store
:表示不缓存响应。no-cache
:表示缓存响应,但是每次使用缓存前必须向服务器验证。
适用场景:
- 静态资源(如CSS、JS、图片):通常这些资源不会频繁变化,可以设置较长的
max-age
。 - API 响应:根据业务需求设置,某些数据(如产品列表)可能每小时更新一次,可以设置适当的
max-age
。 - 用户特定数据:如用户的购物车数据,适合
private
const http = require('http');
const fs = require('fs');
const path = require('path');
http.createServer((req, res) => {
const filePath = path.join(__dirname, 'public', req.url);
fs.readFile(filePath, (err, data) => {
if (err) {
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('Not Found');
return;
}
// 设置强制缓存,例如设置缓存一周
const oneWeekInSeconds = 7 * 24 * 60 * 60;
res.writeHead(200, {
'Content-Type': 'text/html',
// 在浏览器保存指定秒数后过期,优先于Expires
'Cache-Control': `public, max-age=${oneWeekInSeconds}`,
// 兼容旧版浏览器,超过指定日期后过期
'Expires': new Date(Date.now() + oneWeekInSeconds * 1000).toUTCString(),
});
res.end(data);
});
}).listen(3000, () => {
console.log('Server is running on http://localhost:3000');
});
在express中设置静态资源缓存
const express = require('express');
const app = express();
app.use('/static', express.static('public', {
maxAge: '1d', // 强制缓存,缓存一天
etag: true, // 协商缓存
lastModified: true // 协商缓存
}));
app.listen(3000, () => {
console.log('Server is running on http://localhost:3000');
});
在koa中设置静态资源缓存
const Koa = require('koa');
const static = require('koa-static');
const path = require('path');
const app = new Koa();
// 配置静态文件服务
app.use(static(path.join(__dirname, 'public'), {
maxage: 86400000, // 设置静态文件的强制缓存时间,这里是一天,单位是毫秒
index: false // 关闭默认的索引页
}));
// 监听端口
app.listen(3000, () => {
console.log('Server is running on http://localhost:3000');
});