以下是关于强制缓存和协商缓存的详细解释:
1. 强制缓存(Cache-Control 和 Expires)
概念
-
强制缓存:
- 强制缓存是一种缓存策略,当浏览器请求资源时,服务器会在响应头中设置
Cache-Control或Expires字段,告诉浏览器该资源可以缓存多长时间。在缓存有效期内,浏览器将直接从本地缓存中获取资源,而无需向服务器发送请求。
- 强制缓存是一种缓存策略,当浏览器请求资源时,服务器会在响应头中设置
实现方式
-
Cache-Control:
-
Cache-Control是 HTTP/1.1 中的缓存控制字段,具有更多的选项和更灵活的配置。例如:public:表示响应可以被任何缓存(包括代理服务器和客户端)缓存。private:表示响应只能被客户端缓存,不能被代理服务器缓存。max-age:指定缓存的时长(以秒为单位),例如Cache-Control: public, max-age=3600表示资源可以被缓存 1 小时。no-cache:表示在使用缓存前必须先与服务器确认缓存的有效性,并不是不缓存资源。no-store:表示不允许缓存该资源,每次都要从服务器获取。
-
-
Expires:
-
Expires是 HTTP/1.0 中的缓存控制字段,它指定一个绝对的过期时间。例如:Expires: Wed, 21 Oct 2025 07:28:00 GMT
-
由于
Expires是一个绝对时间,可能会受客户端和服务器时间不一致的影响,因此在 HTTP/1.1 中更推荐使用Cache-Control。
-
代码示例
-
服务器端设置(Node.js 示例) :
收起
javascript
const http = require('http');
const fs = require('fs');
http.createServer((req, res) => {
if (req.url === '/') {
res.writeHead(200, {
'Cache-Control': 'public, max-age=3600',
'Content-Type': 'text/html'
});
fs.createReadStream('index.html').pipe(res);
}
}).listen(3000);
-
解释:
- 当客户端请求根路径时,服务器返回
index.html文件,并设置Cache-Control为public, max-age=3600,表示该文件可以被缓存 1 小时。
- 当客户端请求根路径时,服务器返回
2. 协商缓存(Last-Modified 和 ETag)
概念
-
协商缓存:
- 当强制缓存失效后,浏览器会向服务器发送请求,但不是直接请求资源,而是发送一个请求验证缓存是否仍然有效。这是通过
Last-Modified和ETag实现的。
- 当强制缓存失效后,浏览器会向服务器发送请求,但不是直接请求资源,而是发送一个请求验证缓存是否仍然有效。这是通过
实现方式
-
Last-Modified 和 If-Modified-Since:
-
Last-Modified:
-
服务器在响应头中设置
Last-Modified,表示资源的最后修改时间。例如:Last-Modified: Wed, 21 Oct 2025 07:28:00 GMT
-
-
If-Modified-Since:
- 当缓存失效后,浏览器会在请求头中带上
If-Modified-Since,其值为之前服务器返回的Last-Modified值。 - 服务器收到请求后,会将该时间与资源的实际修改时间比较,如果资源未修改,返回 304 Not Modified 状态码,让浏览器继续使用缓存;如果修改了,返回 200 状态码和新资源。
- 当缓存失效后,浏览器会在请求头中带上
-
-
ETag 和 If-None-Match:
-
ETag:
-
服务器在响应头中设置
ETag,它是资源的唯一标识符,通常是一个哈希值或版本号。例如:ETag: "123456789abcdef"
-
-
If-None-Match:
- 当缓存失效后,浏览器在请求头中带上
If-None-Match,其值为之前服务器返回的ETag值。 - 服务器收到请求后,将该
ETag值与当前资源的ETag比较,若相同,返回 304 Not Modified,让浏览器使用缓存;不同则返回 200 状态码和新资源。
- 当缓存失效后,浏览器在请求头中带上
-
代码示例
-
服务器端设置(Node.js 示例) :
收起
javascript
const http = require('http');
const fs = require('fs');
const crypto = require('crypto');
http.createServer((req, res) => {
if (req.url === '/') {
const file = fs.statSync('index.html');
const etag = crypto.createHash('md5').update(fs.readFileSync('index.html')).digest('hex');
const lastModified = file.mtime.toUTCString();
if (req.headers['if-none-match'] === etag && req.headers['if-modified-since'] === lastModified) {
res.writeHead(304);
res.end();
} else {
res.writeHead(200, {
'Content-Type': 'text/html',
'ETag': etag,
'Last-Modified': lastModified
});
fs.createReadStream('index.html').pipe(res);
}
}
}).listen(3000);
-
解释:
- 服务器计算
index.html的ETag(使用 MD5 哈希)和Last-Modified(文件的修改时间)。 - 如果请求头中的
If-None-Match和If-Modified-Since与当前的ETag和Last-Modified相同,返回 304 状态码;否则,返回新资源并更新ETag和Last-Modified。
- 服务器计算
3. 应用场景
强制缓存
-
静态资源:
- 对于长时间不更新的静态资源(如 CSS 文件、JavaScript 文件、图片等),可以使用强制缓存,减少重复请求,提高性能。
协商缓存
-
动态资源或经常更新的资源:
- 对于可能会更新的资源(如 API 数据、动态页面),使用协商缓存可以在资源更新时让浏览器及时获取最新资源,同时避免不必要的资源下载。
4. 总结
-
强制缓存:
- 直接使用本地缓存,减少请求次数,适用于长时间不更新的资源。
-
协商缓存:
-
先与服务器协商,根据资源是否更新决定是否使用缓存,适用于可能更新的资源。
-
在实际的 Web 开发中,根据资源的性质和更新频率选择合适的缓存策略,可以有效提高网站性能和用户体验。同时,根据不同的开发语言和服务器,可以使用不同的工具和框架来设置这些缓存策略。