缓存分为两种:强缓存和协商缓存,根据响应的header内容来决定。
| 获取资源形式 | 状态码 | 发送请求到服务器 | |
|---|---|---|---|
| 强缓存 | 从缓存取 | 200(from cache) | 否,直接从缓存取 |
| 协商缓存 | 从缓存取 | 304(not modified) | 是,通过服务器来告知缓存是否可用 |
强缓存相关字段有expires,cache-control。如果cache-control与expires同时存在的话,cache-control的优先级高于expires。
协商缓存相关字段有Last-Modified/If-Modified-Since,Etag/If-None-Match
1、如何设置浏览器缓存
1.Expires(实体首部字段):服务端返回的到期时间,分为“相对文件的最后访问时间”和“绝对修改时间”。缺点:返回的是服务端时间,比较的是客户端时间,如果浏览器时间与服务器时间相差很大,那么误差就很大。
如果cache-control:max-age和expires同时存在,那么cache-control会设置expires,公式:
Expires = max-age + “每次下载时的当前的request时间”
2.Cache-Control(通用首部字段):
- private:代表允许包括
中间代理服务器以及请求的浏览器缓存 - public:代表只有
请求的浏览器才可以进行缓存。 - max-age=xxx:缓存内容在
xxx秒后失效 - no-cache:需要用
另一种缓存策略来验证缓存(ETag,Last-Modified) - no-store:
不进行缓存 - no-transform(少见):主要是用在proxy服务器,
不允许进行格式转换
3.Last-Modified(实体首部字段):
- 浏览器请求获得文件后,
服务器返回该文件的最后修改时间Last-Modified 下一次请求会带上If-Modified-Since标识,如果If-Modified-Since等于服务器的文件修改时间,则表示文件没有修改,返回304状态码- 浏览器从浏览器缓存中读取文件。如果If-Modified-Since小于服务端的文件修改时间,则浏览器会重新发送请求获取文件,返回200状态码
4.Etag(响应首部字段):服务器文件的唯一标识
- 浏览器请求获得文件后,
服务器返回Etag字段给浏览器,当文件变化时Etag值也会发生变化(Etag其实的文件的Hash值,所以文件改变Hash值也会改变) 下次请求会带上If-None-Match即浏览器保留的ETag值,如果发送了变化,则文件被修改,需要重新请求,返回200状态码- 反之浏览器就从缓存中读取文件,返回304状态码
2、 四者区别
- 当Cache-Control设置为max-age=xx并且同时设置Expires时,
Cache-Control的优先级更高 - 当ETag和Last-Modified同时存在时,服务器先会检查ETag,然后再检查Last-Modified,最终决定返回304还是200,
两者都满足才会返回304,ETag的优先级更高
3、为何既有last-modified又有Etag
考虑以下情况:
- 一些文件也许会周期性的更改,但是他的内容并不改变(仅仅改变的修改时间),这个时候,我们并不希望客户端认为这个文件被修改了,而重新 get
- 某些文件修改非常频繁,比如在秒以下的时间内进行修改(比方说 1s 内修改了 N 次),If-Modified-Since能检查到的粒度时 s 级的,这种修改无法判断(或者说 UNIX 记录 MTIME只能精确到秒)
- 某些服务器不能精确得到的文件的最后修改时间 所以利用Etag可以更准确的控制缓存。
4、启发式缓存
如果一个可以缓存的请求没有设置Expires和Cache-Control,但是响应头有设置Last-Modified信息,这种情况下浏览器会有一个默认的缓存策略:(当前时间 - Last-Modified)*0.1,这就是启发式缓存。
目前看来,大部分浏览器都已经实现了,但是彼此也略有不同。
注:只有在服务端没有返回明确的缓存策略时才会激活浏览器的启发式缓存策略。
启发式缓存会引起什么问题吗??
考虑一个情况,假设你有一个文件没有设置缓存时间,在一个月前你更新了上个版本。这次发版后,你可能得等到3天后用户才看到新的内容了。如果这个资源还在CDN也缓存了,则问题会更严重。
所以,要给资源设置合理的缓存时间。不要不设置缓存,也不要设置过长时间的缓存。
参考链接:blog.csdn.net/weixin_4391…
参考链接:juejin.cn/post/684490…
参考链接:juejin.cn/post/700106…