面试题答案
一键面试缓存策略
- 强缓存:
- 原理:浏览器在加载资源时,先在本地缓存中查找该资源,如果找到且未过期,则直接从缓存中加载,无需向服务器发送请求。
- 适用场景:对于不经常变化的静态资源,如样式表、脚本、图片等,适合使用强缓存。
- 协商缓存:
- 原理:浏览器先向服务器发送请求,服务器根据请求头中的信息判断资源是否有更新。如果资源未更新,服务器返回304状态码,浏览器从本地缓存中加载资源;如果资源已更新,服务器返回200状态码及新的资源。
- 适用场景:对于可能会偶尔更新的资源,适合使用协商缓存,这样既能利用缓存提高性能,又能保证获取到最新的资源。
在Node.js中设置HTTP头实现缓存策略
- 强缓存:
- 设置Cache - Control头:
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); fs.readFile(filePath, (err, data) => { if (err) { res.writeHead(404, {'Content - Type': 'text/plain'}); res.end('Not Found'); } else { // 设置强缓存,例如缓存1小时(3600秒) res.writeHead(200, {'Content - Type': 'text/html', 'Cache - Control':'max - age = 3600'}); res.end(data); } }); }); const port = 3000; server.listen(port, () => { console.log(`Server running on port ${port}`); });
- 设置Expires头:
Expires
头是一个日期时间值,指定资源在浏览器缓存中的过期时间。不过,由于它依赖于客户端和服务器的时间同步,现在更推荐使用Cache - Control
。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); fs.readFile(filePath, (err, data) => { if (err) { res.writeHead(404, {'Content - Type': 'text/plain'}); res.end('Not Found'); } else { const oneHourFromNow = new Date(Date.now() + 3600 * 1000).toUTCString(); res.writeHead(200, {'Content - Type': 'text/html', 'Expires': oneHourFromNow}); res.end(data); } }); }); const port = 3000; server.listen(port, () => { console.log(`Server running on port ${port}`); });
- 设置Cache - Control头:
- 协商缓存:
- ETag:
- 原理:ETag是资源的唯一标识符,由服务器生成。每次资源发生变化,ETag也会改变。浏览器在请求资源时,会在请求头中带上
If - None - Match
字段,其值为之前获取资源时服务器返回的ETag。服务器接收到请求后,对比If - None - Match
的值与当前资源的ETag,如果相同,返回304状态码,否则返回200状态码及新的资源。 - 实现:
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); const fileStat = fs.statSync(filePath); const etag = crypto.createHash('md5').update(fileStat.mtime + fileStat.size).digest('hex'); const ifNoneMatch = req.headers['if - none - match']; if (ifNoneMatch === etag) { res.writeHead(304); res.end(); } else { fs.readFile(filePath, (err, data) => { if (err) { res.writeHead(404, {'Content - Type': 'text/plain'}); res.end('Not Found'); } else { res.writeHead(200, {'Content - Type': 'text/html', 'ETag': etag}); res.end(data); } }); } }); const port = 3000; server.listen(port, () => { console.log(`Server running on port ${port}`); });
- 原理:ETag是资源的唯一标识符,由服务器生成。每次资源发生变化,ETag也会改变。浏览器在请求资源时,会在请求头中带上
- Last - Modified:
- 原理:
Last - Modified
头表示资源的最后修改时间。浏览器在请求资源时,会在请求头中带上If - Modified - Since
字段,其值为之前获取资源时服务器返回的Last - Modified
。服务器接收到请求后,对比If - Modified - Since
的值与当前资源的最后修改时间,如果相同,返回304状态码,否则返回200状态码及新的资源。 - 实现:
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); const fileStat = fs.statSync(filePath); const lastModified = fileStat.mtime.toUTCString(); const ifModifiedSince = req.headers['if - modified - since']; if (ifModifiedSince === lastModified) { res.writeHead(304); res.end(); } else { fs.readFile(filePath, (err, data) => { if (err) { res.writeHead(404, {'Content - Type': 'text/plain'}); res.end('Not Found'); } else { res.writeHead(200, {'Content - Type': 'text/html', 'Last - Modified': lastModified}); res.end(data); } }); } }); const port = 3000; server.listen(port, () => { console.log(`Server running on port ${port}`); });
- 原理:
- ETag:
处理缓存更新的情况
- 强缓存更新:
- 修改文件名:对于不经常变化的静态资源,可以在资源文件名中加入版本号或哈希值。例如,将
styles.css
改为styles_v1.0.css
或styles_abc123.css
。当资源内容发生变化时,更新文件名,这样浏览器会把它当作新的资源请求,不会使用旧的缓存。 - 控制缓存时间:合理设置
Cache - Control
的max - age
值。如果资源更新比较频繁,可以将max - age
设置得较短,如几分钟或几小时;如果资源更新不频繁,可以设置较长的时间,如一天或一周。
- 修改文件名:对于不经常变化的静态资源,可以在资源文件名中加入版本号或哈希值。例如,将
- 协商缓存更新:
- 更新ETag或Last - Modified值:当资源内容发生变化时,重新计算ETag值(如通过文件内容的哈希计算),或者更新
Last - Modified
的时间戳。这样浏览器下次请求时,由于ETag或Last - Modified
值与之前不同,会获取新的资源。
- 更新ETag或Last - Modified值:当资源内容发生变化时,重新计算ETag值(如通过文件内容的哈希计算),或者更新