由于本周因为相关缓存(cdn缓存和浏览器缓存)问题,导致部分页面数据出现点异常(数据显示之前的旧数据),因此打算去验证下,加深理解。
1、浏览器缓存:
- 浏览器缓存主要是指
http缓存
,当客户端向服务器请求资源时,会优先选择浏览器缓存。如果浏览器有存在“要请求资源
”的副本,就可以直接从浏览器缓存中提取,而不用从服务器获取这个资源。 - 一般
http缓存
能缓存get
请求响应的资源,通常指Web资源
(html页面、图片、js、css等) http缓存
都是从第二次
请求开始的。第一次请求资源,服务器返回的响应头上会有缓存参数
设置。通过第二次请求资源时,浏览器会通过这些参数自动判断是否从缓存读取- 首先会判断是否是
强缓存
,请求直接返回200
。 - 如果没有
强缓存
,则判断是否命中协商缓存
,如果命中返回304
。 - 如果两个都没有命中,返回新资源)
- 首先会判断是否是
- 使用
express
服务器的静态服务器,去做对应相关的检验处理。
express.static(root, [options]) //静态资源设置
//通过对于额外参数options修改,
//可以设置请求对应静态资源的缓存参数。
//其中与缓存相关参数有etag,cacheControl,lastModified,maxAge,setHeaders
在public文件夹下,新增个other.html文件,javascripts文件夹新增other.js,由other.html引用。然后设置静态资源,默认不使用缓存:
app.use(express.static(path.join(__dirname, 'public'), {
etag: false,
cacheControl: false,
lastModified: false
}));
结果如图所示,基本响应是200,资源每次都是从服务器获取。
2、http请求示意图
首次请求流程图
再次请求流程图
3、强缓存
- 强缓存是不需要发送http请求
- 强缓存在http1.0,是由Expires控制
app.use(express.static(path.join(__dirname, 'public'), {
etag: false,
cacheControl: false,
lastModified: false,
setHeaders: function (res) {
const time = new Date(new Date().getTime() + 30 * 1000);
res.append("Expires", time.toGMTString())//设置30s过期日期
}
}));
如下图所示,第一次请求,会从服务器读取,在过期日前之前,重新请求,发现后台服务器没有other.js的请求,且浏览器客户端请求是从内存缓存中读取。
注意:由于服务器时间与浏览器时间可能不一致,导致服务器返回的过期日期可能存在不准确情况。
- 强缓存在http1.1,是由Cache-Control控制
app.use(express.static(path.join(__dirname, 'public'), {
etag: false,
//cacheControl: false,//启用cacheControl
maxAge: 10000,//设置10s过期
lastModified: false,
setHeaders: function (res) {
const time = new Date(new Date().getTime() + 30 * 1000);
res.append("Expires", time.toGMTString())
}
}));
如下图所示,当Cache-Control
与Expires
同时存在,优先由Cache-Control
决定。
注意:public 中主要参数由
public参数 | 使用 |
---|---|
public | 客户端和代理服务器都可以缓存。因为一个请求可能要经过不同的`代理服务器`最后才到达目标服务器,那么结果就是不仅仅浏览器可以缓存数据,中间的任何代理节点都可以进行缓存 |
private | 只有浏览器能缓存了,中间的代理服务器不能缓存 |
no-cache | 跳过当前的强缓存,发送HTTP请求,即直接进入`协商缓存阶段 |
no-store | 非常粗暴,不进行任何形式的缓存 |
s-maxage | 这和`max-age`长得比较像,但是区别在于s-maxage是针对代理服务器的缓存时间。 |
4、协商缓存
- 强缓存失效后,浏览器会根据请求头中相应的标识向服务器请求,服务器根据这个标识来决定是否需要使用浏览器缓存。这个标识分为两种
Last-Modified
和ETag
。 Last-Modified
app.use(express.static(path.join(__dirname, 'public'), {
etag: false,
//cacheControl: false,
maxAge: 10000,
//lastModified: false,
setHeaders: function (res) {
const time = new Date(new Date().getTime() + 30 * 1000);
res.append("Expires", time.toGMTString())
res.append("Cache-control", "no-cache")
}
}));
即最后修改时间。在浏览器第一次给服务器发送请求后,服务器会在响应头中加上这个字段。
浏览器接收到后,如果再次请求,会在请求头中携带If-Modified-Since
字段,这个字段的值也就是服务器传来的最后修改时间。服务器拿到请求头中的If-Modified-Since
的字段后,其实会和这个服务器中该资源的最后修改时间
对比:
- 如果请求头中的这个值小于最后修改时间,说明是时候更新了。返回新的资源,跟常规的HTTP请求响应的流程一样。
- 否则返回304,告诉浏览器直接用缓存。
第一次请求,如图所示:
第二次请求,如图所示:
修改文件后,在请求,会发现服务器上文件的时间不一样了,从而重新请求服务器资源。
注意:Last-Modified 能够感知的单位时间是秒,如果文件在 1 秒内改变了多次,那么这时候的 Last-Modified 并没有体现出修改了
ETag
app.use(express.static(path.join(__dirname, 'public'), {
//etag: false,
//cacheControl: false,
//maxAge: 10000,
lastModified: false,
setHeaders: function (res) {
const time = new Date(new Date().getTime() + 30 * 1000);
res.append("Expires", time.toGMTString())
res.append("Cache-control", "no-cache")
}
}));
ETag是服务器根据当前文件的内容,给文件生成的唯一标识,只要里面内容发生改变,这个值就会不一样,服务器通过响应头把这个值给浏览器。浏览器通过这个ETag,再下次一次请求时候,把这个值作为If-None-Match这个字段的内容,并放到请求头中,然后发给服务器。 服务器接收到If-None-Match后,会跟服务器上该资源的ETag进行比对:
- 如果两者不一样,说明要更新了。返回新的资源,跟常规的HTTP请求响应的流程一样。
- 否则返回304,告诉浏览器直接用缓存。
第一次请求:
第二次请求:
修改内容后,会发现重新请求服务端:
注意:
ETag
优先级高于Last-Modified
。ETag
(针对于文件的内容生成的哈希值)性能会低于Last-Modified
(通过文件编辑的时间)
5、缓存存储位置
缓存存储位置 | 含义 |
---|---|
Service Worker | 主要用于离线缓存,无法访问浏览器的dom |
Memory Cache | 内存缓存,从效率上讲它是最快的。但是从存活时间来讲又是最短的,当渲染进程结束后,内存缓存也就不存在了(比较小的文件) |
Disk Cache | 磁盘中的缓存,从存取效率上讲是比内存缓存慢的,但是他的优势在于存储容量和存储时长(内存使用率高情况,或者文件比较大情况使用) |
Push Cache | 推送缓存 |
6、缓存好处
- 减少了冗余的数据传输,节省了网费。
- 缓解了服务器的压力, 大大提高了网站的性能
- 加快了客户端加载网页的速度
7、不使用缓存操作
1、使用ctrl+F5 强制刷新,会不使用强缓存和协商缓存
2、通过浏览器设置,停用缓存,每次都是从服务器获取最新数据
3、在强缓存的日期未过期,不需要从缓存数据中获取,可以在url后面增加时间戳等
参考文献: