Web开发中的强缓存和协商缓存策略
Web开发中的强缓存和协商缓存策略
一. 概念
前端缓存是Web
开发中非常重要的一部分,它可以提高网站的性能和用户体验。其中,强缓存和协商缓存是两种常见的缓存方式。
获取资源形式 | 状态码 | 发送到服务器 | |
---|---|---|---|
强缓存 | 从缓存获取 | 200 (from cache) | 否,直接从缓存获取 |
协商缓存 | 从缓存获取 | 304 (not modified) | 是,通过服务器来告知缓存是否可用 |
- 强缓存的相关字段有
expires
,cache-control
。如果cache-control
和expires
同时存在的话,cache-control
的优先级高于expires
- 协商缓存的相关字段有
Last-Modified/If-Modified-Since,Etag/If-None-Match
- 浏览器第一次发起请求时,本地无缓存,向web服务器发送请求,服务器端响应请求,浏览器端缓存,过程如下:
第一次请求时,服务器会将页面最后的修改时间通过last-Modified
标识发送给客户端,客户端记录修改时间;服务器还会生成一个Etag
,并发送给客户端。
- 浏览器后续再次请求:
-
浏览器请求某一资源时,会先获取该资源缓存的header信息,然后根据
header
中的Cache-Control
和Expires来判断是否过期
。若没过期直接从缓存中获取资源信息,包括缓存的header
的信息,所以此次请求不会和服务器进行通信。这里判断是否过期,则是强缓存相关。 -
如果显示已过期,浏览器会向服务器端发送请求,这个请求会携带第一次请求返回的有关缓存的
header
字段信息,比如客户端会通过If-None-Match
头将先前服务器端发送过来的Etag
发送给服务器,服务器会对比这个客户端发过来的Etag
是否与服务器相同,若相同,就将If-None-Match
的值设置为false,返回状态304,客户端继续使用本地缓存,不解析服务器端返回的数据;若不相同就将If-None-Match
的值设为true,返回状态200,客户端重新接收服务器端返回的数据;客户端还会通过If-Modified-Since
头将先前服务器端发过来的最后修改时间戳发给服务器,服务器端通过这个时间戳判断客户端的页面是否是最新的,如果不是最新的,则返回最新的内容,否之返回304,客户端继续使用本地缓存。
1.强缓存(本地缓存)
强缓存是指浏览器在第一次请求资源时,将资源缓存到本地,并在下一次请求时直接从缓存中获取,而不是向服务器发送请求。这种缓存方式可以通过设置HTTP
头中Cashe-Control
和Expires
字段来实现。其中,Cashe-Control
可以设置缓存的最大时间,而Expires
则是设置缓存的过期时间。当缓存时间未过期时,浏览器会直接从缓存中获取资源,从而提高网站的访问速度。
2.协商缓存(弱缓存)
协商缓存是浏览器在第一次请求资源时,服务器会返回一个包含资源信息的响应头,浏览器会将信息保存在缓存中。下一次请求时,浏览器会将这些信息发送给服务器,服务器会根据这些信息判断资源是否更新。如果没有更新,则返回一个304 Not Modified
的响应,告诉浏览器可以直接从缓存中获取资源。这种缓存方式可以通过设置HTTP响应头中的ETag
和Last-Modified
字段来实现。
二. 使用场景
1. 下面是一个前端强缓存的例子
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>前端强缓存示例</title>
</head>
<body>
<h1>前端强缓存示例</h1>
<img src="/image.jpg" alt="图片">
<script src="/script.js"></script>
</body>
</html>
const express = require('express');
const app = express();
// 设置静态文件目录
app.use(express.static('public'));
app.get('/index.html', (req, res) => {
res.sendFile(__dirname + '/public/index.html');
});
// 设置图片的缓存时间为1小时
app.get('/image.png', (req, res) => {
res.setHeader('Cache-Control', 'max-age=3600');
res.sendFile(__dirname + '/public/image.png');
});
// 设置JavaScript文件的缓存时间为1天
app.get('/script.js', (req, res) => {
res.setHeader('Cache-Control', 'max-age=86400');
res.sendFile(__dirname + '/public/script.js');
});
// 启动服务器
app.listen(3000, () => {
console.log('http://127.0.0.1:3000', '正在启动......');
})
在上面的服务器端代码中,使用了Express
框架设置了静态文件目录,并分别
设置了图片和JavaScript
文件的缓存时间为1小时和1天。当浏览器第一次请求这些资源时,服务器会将资源发给浏览器,并在响应头中设置缓存时间和缓存策略。当浏览器再次请求这些资源时,会检查缓存是否过期,如果没有过期,则直接从缓存中获取资源,否则重新向服务器请求资源。
2. 下面是一个前端协商缓存的例子
const express = require('express');
const app = express();
// 设置静态文件服务
app.use(express.static('public'));
// 设置路由,返回一个JSON数据
app.get('/api/data', (req, res) => {
const data = { message: 'Hello, world!' };
// 设置响应头部,包括缓存控制和协商缓存相关的头部
res.set({
'Cache-Control': 'public, max-age=3600', // 设置强缓存,缓存时间为1小时
'Last-Modified': new Date().toUTCString(), // 设置最后修改时间
'ETag': '123456789' // 设置ETag
});
res.json(data);
});
// 设置路由,用于协商缓存
app.get('/api/data2', (req, res) => {
const data = { message: 'Hello, world!' };
// 获取请求头部中的If-Modified-Since和If-None-Match字段
const ifModifiedSince = req.headers['if-modified-since'];
const ifNoneMatch = req.headers['if-none-match'];
// 如果两个字段都存在,说明浏览器支持协商缓存
if (ifModifiedSince && ifNoneMatch) {
// 比较最后修改时间和ETag是否匹配
if (ifModifiedSince === new Date().toUTCString() && ifNoneMatch === '123456789') {
// 如果匹配,返回304 Not Modified状态码,表示资源未修改,可以使用缓存
res.status(304).end();
return;
}
}
// 如果不支持协商缓存或者资源已经修改,返回新的数据和响应头部
res.set({
'Cache-Control': 'public, max-age=3600',
'Last-Modified': new Date().toUTCString(),
'ETag': '123456789'
});
res.json(data);
});
// 启动服务器
app.listen(3000, () => {
console.log('http://127.0.0.1:3000', '正在启动......');
})
在上面的代码中,当浏览器请求/api/data2
路由时,服务器会先获取请求头部中的If-Modified-Since
和If-None-Match
字段,如果两个字段都存在,说明浏览器支持协商缓存。服务器会比较最后修改时间和ETag是否匹配,如果匹配,返回304 Not Modified状态码,表示资源未修改,可以使用缓存。如果不支持协商缓存或者资源已经修改,服务器会返回新的数据和响应头部,包括Cache-Control
、Last-Modified
和ETag
等头部,用于强缓存和协商缓存。这样,浏览器就可以根据响应头部中的信息来判断是否使用缓存。
三. 总结
在实际开发中,我们可以根据资源的特点和需求来选择合适的缓存方式。
- 强缓存:适用于不经常更新的静态资源,如图片、
CSS
和JS
文件等 - 协商缓存:适用于经常更新的动态资源,如
HTML
页面和API
接口
转载自:https://juejin.cn/post/7239630585500876857