Node.js HTTP 缓存
HTTP 缓存分两大类:强缓存和协商缓存,通过 HTTP 响应头控制,目的是提升性能。
强缓存
不向服务器发请求,直接从浏览器缓存读取。
存储位置
| 类型 | 位置 | 特点 |
|---|---|---|
| memory cache | 浏览器内存 | 快但容量小,刷新页面可见 |
| disk cache | 硬盘 | 容量大但读取较慢 |
Expires(HTTP 1.0,已过时)
js
res.setHeader('Expires', new Date(Date.now() + 60000).toUTCString())缺点:依赖本地时间,客户端时间不准会导致缓存失效。
Cache-Control(HTTP 1.1,主流)
js
// 禁止缓存
res.setHeader('Cache-Control', 'no-cache')
// 允许缓存 60 秒(推荐)
res.setHeader('Cache-Control', 'max-age=60')max-age 是相对时间(秒),比 Expires 更可靠。
协商缓存
强缓存失效后,浏览器会发请求给服务器验证,服务器判断资源是否变化。
Last-Modified / If-Modified-Since
js
// 服务端:第一次响应带上最后修改时间
res.setHeader('Last-Modified', stats.mtime.toUTCString())
// 浏览器下次自动带 If-Modified-Since
// 服务端对比:
const ifModifiedSince = req.headers['if-modified-since']
if (ifModifiedSince && new Date(ifModifiedSince) >= stats.mtime) {
res.statusCode = 304 // 未修改,继续用缓存
return res.end()
}
// 否则返回新内容...(状态码 200)ETag / If-None-Match(更精确)
js
import crypto from 'node:crypto'
const content = fs.readFileSync(filePath)
const etag = crypto.createHash('md5').update(content).digest('hex')
res.setHeader('ETag', etag)
// 浏览器下次自动带 If-None-Match
// 服务端对比:
if (req.headers['if-none-match'] === etag) {
res.statusCode = 304 // 未修改
return res.end()
}强缓存 vs 协商缓存
| 强缓存 | 协商缓存 | |
|---|---|---|
| 请求服务器 | 不发请求 | 发请求验证 |
| 状态码 | 200 (from cache) | 304 (Not Modified) |
| 头部 | Cache-Control / Expires | Last-Modified / ETag |
| 适用场景 | 不常变的静态资源(JS/CSS/图片) | 可能变化但希望减少传输 |