
在 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;}// 生成 ETagconst 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;}// 设置 ETagres.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 |
确保客户端获取到最新的文件内容 | 需要向服务器发送请求进行验证,增加了一定的开销 | 可能会频繁更新的静态文件 |
通过合理配置静态文件缓存,可以在提升应用性能的同时,确保客户端始终获取到最新的文件内容。在实际开发中,可以根据文件的更新频率和业务需求选择合适的缓存策略。