在 Node.js 中构建静态文件服务时,合理配置静态文件缓存可以显著提升应用的性能和响应速度。本文将深入探讨如何在 Node.js 中配置静态文件缓存,以及不同缓存策略的应用场景和实现方法。
在 Web 应用中,静态文件(如 CSS、JavaScript、图片等)通常不会频繁更新。如果每次用户请求这些文件时都从服务器重新获取,会增加服务器的负载和用户的等待时间。通过使用缓存,浏览器可以将这些文件存储在本地,下次请求时直接从本地读取,从而减少不必要的网络请求,提高页面加载速度。
常见的缓存策略有两种:强缓存和协商缓存。
强缓存是指浏览器直接从本地缓存中读取文件,而不需要向服务器发送请求。通过设置响应头 Cache-Control
和 Expires
来控制强缓存。
Cache-Control
:是一个通用的缓存控制头,用于指定缓存的行为,如 max-age
表示缓存的最大时间(以秒为单位)。Expires
:是一个 HTTP 1.0 时代的缓存控制头,指定缓存的过期时间,不过由于它依赖于客户端和服务器的时间同步,现在一般使用 Cache-Control
替代。协商缓存是指浏览器在使用本地缓存之前,会先向服务器发送一个请求,询问服务器该文件是否有更新。如果文件没有更新,服务器返回 304 状态码,浏览器直接使用本地缓存;如果文件有更新,服务器返回 200 状态码和新的文件内容。通过设置响应头 ETag
和 Last-Modified
来控制协商缓存。
ETag
:是一个文件的唯一标识符,服务器可以根据文件的内容生成一个 ETag。Last-Modified
:指定文件的最后修改时间。以下是一个使用 Node.js 原生 http
模块实现强缓存的示例代码:
const http = require('http');
const fs = require('fs');
const path = require('path');
const server = http.createServer((req, res) => {
const filePath = path.join(__dirname, 'public', req.url === '/'? 'index.html' : req.url);
fs.stat(filePath, (err, stats) => {
if (err) {
res.statusCode = 404;
res.end('File not found');
return;
}
// 设置强缓存,缓存时间为 3600 秒(1 小时)
res.setHeader('Cache-Control', 'public, max-age=3600');
fs.createReadStream(filePath).pipe(res);
});
});
server.listen(3000, () => {
console.log('Server is running on port 3000');
});
在上述代码中,我们通过设置 Cache-Control
头来控制强缓存,将缓存时间设置为 3600 秒。
以下是一个使用 Node.js 原生 http
模块实现协商缓存的示例代码:
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, 'public', req.url === '/'? 'index.html' : req.url);
fs.readFile(filePath, (err, data) => {
if (err) {
res.statusCode = 404;
res.end('File not found');
return;
}
// 生成 ETag
const hash = crypto.createHash('md5').update(data).digest('hex');
const etag = `"${hash}"`;
const ifNoneMatch = req.headers['if-none-match'];
if (ifNoneMatch === etag) {
res.statusCode = 304;
res.end();
return;
}
// 设置 ETag
res.setHeader('ETag', etag);
res.end(data);
});
});
server.listen(3000, () => {
console.log('Server is running on port 3000');
});
在上述代码中,我们通过生成文件的 MD5 哈希值作为 ETag,并在响应头中设置 ETag
。当浏览器再次请求该文件时,会在请求头中携带 If-None-Match
字段,服务器比较 If-None-Match
和 ETag
的值,如果相同则返回 304 状态码,否则返回新的文件内容。
缓存策略 | 控制头 | 优点 | 缺点 | 适用场景 |
---|---|---|---|---|
强缓存 | Cache-Control 、Expires |
减少服务器请求,提高页面加载速度 | 缓存时间内文件更新后客户端无法及时获取 | 不经常更新的静态文件,如图片、CSS、JavaScript 等 |
协商缓存 | ETag 、Last-Modified |
确保客户端获取到最新的文件内容 | 需要向服务器发送请求进行验证,增加了一定的开销 | 可能会频繁更新的静态文件 |
通过合理配置静态文件缓存,可以在提升应用性能的同时,确保客户端始终获取到最新的文件内容。在实际开发中,可以根据文件的更新频率和业务需求选择合适的缓存策略。