目录
1、Last-Modified/If-Modified-Since
缓存一般分为浏览器缓存(前端)和http缓存(后端)。当然前端也可以http缓存,后面会写,只不过一般还是后端使用http缓存
浏览器缓存(本地存储):localStorage、sessionStorage、cookie等
一般用来存一些必要的数据,比如token、列表数据、传递给后端的参数等等,但缓存的都是只能在5M左右的数据,像cookie还要小,只能缓存4kb这样。
http缓存(强缓存、协商缓存):缓存一些副本,比如html、img、css等静态资源
主要解决一些加载的问题:加快加载页面的速度、减少不必要的网络传输(节约宽带)、减少服务器负载,避免服务器过载的问题(减载)
当然,无论是哪种缓存,都会有一个缺点,那就是占内存。所以在使用的时候还是要注意。
强缓存有两个字段:Expires、Cache-control
Expires是绝对时间,表示在一个时间点之前发起请求则直接在浏览器中获取数据
Cache-control是相对时间,表示与上一个请求之后的多少秒之内请求,则直接获取浏览器中的缓存数据
这里要注意,由于Expires是根据浏览器的系统时间来决定的,有不准确性,所以才有了Cache-control,因此Cache-control的优先级比Expires高。一般会使用Cache-control的多,但也有使用Expires的情况,那就是浏览器版本低的问题,兼容性较差则使用Expires
设定一个时间,在这个时间范围内,都是从浏览器内存中读取,这个时间是根据电脑的系统时间进行判定的,所以要注意,如果系统时间和服务器时间不对,则会影响缓存
Expires:new Date("2022-12-30 23:59:59");
在请求时,浏览器会先判断在没有失效的情况下,也就是缓存时间没有过期的情况下,内存中是否有缓存数据,有会直接使用内存中的数据,没有才会请求服务器。
返回的状态码是200
我们可以使用请求头/响应头的Cache-control字段来实现强缓存
'Cache-Control':'max-age=10'
这个代码的意思是只要在第一次请求/响应返回的时候开始,如果10秒内再次请求,则会直接从缓存中读取。
Cache-control的对应属性:
属性 | 属性介绍 |
max-age | 客户端资源被缓存的最大周期(超过这个周期呗认为是过期) |
s-maxage | 代理服务器缓存的时长(注意:会覆盖max-age和expires) |
no-cache | 强制进行协商缓存(跳过强缓存,直接去服务器进行协商缓存) |
no-store | 禁止任何缓存策略 |
public | 资源可以被浏览器缓存也可以被代理服务器缓存 |
private | 资源只能被浏览器缓存 |
补充:
max-age和s-maxage可以同时出现,s-maxage必须和public一起使用
no-cache和no-store两者只能选一个使用,不能同时出现
public和private两者只能选一个使用,不能同时出现,若两个都没选,则默认是private
Cache-control多个值一起使用,可以用逗号隔开
Cache-control:max-age=100,s-maxage=100,public
当第一次请求服务器的时候没有Cache-Control和Expires、过期的时候,浏览器第二次请求的时候就会和服务器进行协商,会和服务器的资源进行对比,是否有修改,没有修改就烦状态码304,然后继续使用浏览器中的数据;有修改就会犯状态码200,然后返回新数据并同时更新浏览器中的数据。
协商缓存的的字段如下:ETag\If-Not-Match、Last-Modified\If-Modified-Since
两组字段,ETag的优先级会更高一点
第一次请求的时候,返回文件以及文件最新修改的时间,然后浏览器缓存下来,第二次请求的时候先判断有没有过期,没有过期,还是使用浏览器缓存的数据;过期了,再判断一下文件最新修改的时间If-Modified-Since和缓存的时间Last-Modified是否一致,如果时间一致表示文件没有修改过则返回状态码304,继续使用浏览器中的数据,时间不一致表示文件修改了则返回状态码200并返回最新的文件以及最新修改的时间
Last-Modified是浏览器上一次缓存的时间也就是上一次服务器响应的时间;If-Modified-Since是给服务器的时间也就是第二请求时发送的时间
- const data =fs.readFileSync('./img1.png') //读取资源
- const {times}=fs.statSync('./img1.png') //读取修改时间
- res.setHeader('last-modified',times.toUTCString()) //设置协商缓存
- res.setHeader('Cache-control','no-cache') //设置协商缓存
补充:
1秒以内的修改是无法识别的
某些服务器不能准确的识别
只要有修改,哪怕像文件名之类修改了,都会重新向服务器请求
为了解决上面的问题,因此有了一组新的字段,从比较时间戳换成比较文字指纹,这样会更加精确点
ETag存储了文件的特殊标识(一般是hash,也就是根据文件内容计算出的唯一哈希值),整个流程还是和上面的Last-Modified一样,只不过把字段Last-Modified换成ETag,与之对应的内容更新修改时间换成特殊标识hash,同时If-Modified-Since字段也换成If-Not-Match
hash:就是我们打包完文件后,发现文件名那里多了一串乱码,那个乱码就是哈希值
- const data =fs.readFileSync('./img1.png') //读取资源
- const etag=etag(data) //生成唯一标识
- req.headers['if-none-macth'] //获取上一次的hash
补充:
由于hash需要进行计算,所以会有性能消耗
分布式服务器存储时,计算etag的算法如果不一样,这样会导致浏览器从一台服务器获得的数据后到另一台服务器进行验证etag的时候会出现不匹配的情况可以识别1秒内的修改
前端设置静态html页面缓存方法:使用标签
- // 其他浏览器识别的标签
- <meta http-equiv="Cache-Control" content="max-age=7200" />
-
-
- // 仅有IE浏览器才识别的标签
- <meta http-equiv="Expires" content="Mon, 20 Jul 2013 23:00:00 GMT" />
静态资源缓存的方法:则是一般是在web服务器上配置的,常用的web服务器有:nginx、apache等
用户操作 | Expires/Cache-Control | Last-Modified/Etag |
地址栏回车 | 有效 | 有效 |
页面链接跳转 | 有效 | 有效 |
新开窗口 | 有效 | 有效 |
前进、后退 | 有效 | 有效 |
F5刷新 | 无效 | 有效 |
Ctrl+F5刷新 | 无效 | 无效 |