强缓存与协商缓存
最近一直在面试,也算是查漏补缺吧,好多知识点都学过用过,但是说的不够系统,这个算其中一个,我还是经常总结分享吧。
1.概念
首先要理解这两种缓存都是浏览器的与服务器的协议,都发生在浏览器第一次请求发生后,再次请求时,使用本地缓存的策略。
强缓存:浏览器第一次请求资源时将资源缓存到本地。第二次检查资源是否过期。如果没有过期,则直接使用资源,如果过期,则重新请求请求。 协商缓存:浏览器第一次请求缓存,保存缓存ID和时间。重复请求。浏览器会要求服务器验证资源是否更新。服务器根据缓存ID进行判断。如果没有失效,则使用缓存。在这种情况下,返回 304。通知客户端缓存数据可用。如果失败,则重新发送资源。
一般都是做强缓存的。只有当强缓存失败时,才协商缓存。
缓存 | 状态码 | 请求服务器 |
---|---|---|
强缓存 | 200 | 不,直接从缓存中获取 |
协商缓存 | 304 | 是的,通过服务器告诉浏览器缓存是否可用 |
2.强缓存
对于强缓存,响应头中会有两个字段来表示失效规则(Expires/Cache-Control)
1.Expires
Expires 的值是服务器返回的过期时间,即下一次请求时间小于服务器返回的过期时间,直接使用缓存的数据。 Expires 是 HTTP 1.0 的产物,现在默认浏览器都默认使用 HTTP 1.1,所以这里不做赘述。
2.Cache-Control
具体参数定义在rfc2616上可以查到。
值有private
、public
、no-cache
、max-age
,no-store
等等
值 | 作用 |
---|---|
private | 响应消息的全部或部分是针对单个用户的,不得由共享缓存(例如代理服务器)缓存。 |
public | 客户端和代理服务器都可以缓存 |
max-age=xxx | 缓存内容将在 xxx 秒后过期 |
no-cache | 跳过设置强缓存,但不会阻止设置协商缓存 |
no-store | 不缓存,这样会使客户端和服务器都不缓存,所以没有所谓的强缓存,协商缓存 |
immutable | 如果请求资源,则直接读取缓存,即使用户进行刷新操作,也不会将请求发送到服务器 |
3.pragma
Pragma
是一个 HTTP/1.0 标头。 Pragma: no-cache
就像 Cache-Control: no-cache
它强制缓存在释放缓存副本之前将请求提交给源服务器进行验证。但是,Pragma
没有为 HTTP 响应指定,因此不是通用 HTTP/1.1Cache-Control
标头的可靠替代品。
Pragma
应该仅用于与 HTTP/1.0 缓存的向后兼容,其中 Cache-Control
HTTP/1.1 的header尚不存在。
3.协商缓存
对于协商缓存,响应头中会有两个字段来表示(Last-Modified / ETag)
1.Last-Modified
Last-Modified 的值表示该响应资源的最后修改时间。响应请求时,Web 服务器告诉浏览器资源的最后修改时间。再次请求资源时,请求头会包含If-Modified-Since
域,值在Last-Modified
之前返回,如:If-Modified-Since: Mon, 06 Jul 2020 0610 GMT
。服务器会比较这个字段的最后修改时间和资源。如果一致,则证明没有被修改,告诉浏览器直接使用缓存,返回304;如果不一致,直接返回修改后的资源,并将Last-Modified修改为新值。
缺点
- 只要是编辑过的,不管内容是否真的发生了变化,都会以上次修改的时间作为判断依据,作为新的资源返回,造成对请求的不必要的响应,也就是原来缓存的作用是避免不必要的请求。
- if-Modified-由于时间精度只能达到秒,有些文件修改的非常频繁,比如在不到秒的时间内,(比如1s内修改了N次),这个是无法判断的。
2.ETag
为了解决Last-Modified的上述问题,可以使用用ETag
每个文件都有一个ETag
。当您更改文件时,它会更改。它是一个文件哈希,每个文件都是唯一的。就像用webpack打包的时候,每个资源都会有这个东西。比如:app.js 打包后变成了built.85d2fsbf33.js,加上唯一的hash也是为了解决缓存问题。
协商过程
- 发送请求->查看资源是否过期->过期->请求服务器->服务器比较资源是否真的过期->未过期->返回304状态码->客户端缓存使用的旧资源。
- 发送请求 -> 查看资源是否过期 -> 过期 -> 请求服务器 -> 服务器比较资源是否真的过期 -> 过期 -> 返回 200 状态码 -> 客户端就像第一次收到资源,在缓存控制中记下它的 Max-age、etag、last-modified 等。
请求资源时,同时将用户本地的资源etag带到服务器,服务器与最新的资源进行比较。 如果资源没有变化,返回304,浏览器读取本地缓存。 如果资源发生了变化,则返回200,并返回最新的资源。
3.访问刷新分析
访问和刷新分为以下三种情况:
- 输入URL
- 按刷新键,F5刷新,在网页上右键重新加载
- ctrl + F5 强制刷新
1.输入URL
在这种情况下,将使用缓存策略的实际设计来判断。
- 如果你先走强缓存路线。根据cache-control(expires的优先级低)判断缓存是否已经过期。如果它没有过期,则返回 200(从缓存中)。
- 如果本地缓存已经过期再去协商缓存路由,根据之前的last-modified值与服务器比较
- 如果此时间后没有变化,则读取本地缓存并返回304。
- 否则,返回新资源,状态码 200,并更新返回响应的最后修改值。
2.按刷新键,F5刷新,在网页上右键“重新加载
在这种情况下,浏览器实际上会将cache-control
的max-age
直接设置为0
,让缓存立即过期,直接去协商缓存路由。
3.CTRL+ F5 强制刷新
在强制刷新的情况下,浏览器会强制设置no-cache
为强制访问最新资源,甚至直接去掉了If-Modified-Since/If-None-Match等请求头。
4.请求头
在上文中提及到了If-Modified-Since/If-None-Match等请求头,该请求头的值是浏览器在发送第一次请求后从响应头中获取的。
1.If-Modified-Since
该域对应第一次请求时浏览器返回的Last-Modified的值,浏览器将获取到的该值返回给服务器,如果该URI没有更新的话会返回304让浏览器取本地缓存,反之如果有更新的话会返回200并且会更新If-Modified-Since的值。
2.If-None-Match
同样的,这里对应第一次从服务器获取的ETag的值,在后面的请求中发送给服务器并且由服务器进行比较,如果不相同的话会返回200并且会返回新的ETag,如果没有变化则返回304。