缓存策略
- 强缓存
- 协商缓存
优先级较高的是强缓存,当强缓存失效的情况下,才会走协商缓存流程
都是通过设置 HTTP Header 来实现的
强缓存
特点
- 不会向服务器发送网络请求,直接从缓存中读取资源
- 请求返回200的状态码
- 在devtools的network选项卡可以看到size显示from disk cache或from memory cache
设置方法
通过两种 HTTP Header 实现
- Expires
- Cache-Control
二者区别
- Expires 是RFC 2616(HTTP/1.0)协议中和网页缓存相关字段
- Cache-Control 是HTTP/1.1 中实现缓存机制的指令
- Cache-Control优先级高于Expires
Expires
使用举例:
Expires : Wed Mar 04 2020 13:33:10 GMT
用来指定资源的到期时间,表示资源在
- 这个时间之前无需发起网络请求,直接使用缓存的资源
- 这个时间之后失效,需要重新发起网络请求获取
缺陷: 受限制与本地时间,可以通过修改本地时间致其失效
Cache-Control
使用举例:
Cache-control: max-age=30
表示资源会在 30 秒后过期,需要再次请求,max-age是距离请求发起的时间的秒数
Cache-Control 生于 HTTP/1.1,优先级高于 Expires 。
可以在请求头
或者响应头
中设置,并且可以组合使用多种指令
常见指令
指令 | 作用 |
public | 响应可以被服务端或者客户顿缓存 |
private 默认值 | 响应只可以被客户端缓存 |
max-age=30 | 缓存30s后过期需要重新请求 |
s-maxage=30 | 覆盖max-age,作用一致,代理服务器才生效 |
no-store | 不缓存任何响应 |
no-cache | 资源能被缓存,但立即失效 |
max-stale=30 | 30s内,即使缓存过期也使用该缓存 |
min-fresh=30 | 希望30s内获取最新的响应 |
特点 优先级高于Expires,指令可以组合
协商缓存
协商缓存就是强缓存失效后,浏览器携带资源的缓存标识向服务器发起请求,由服务器根据缓存标识决定是否继续使用缓存的过程
当浏览器发起请求验证资源时,如果资源没有做改变,那么服务端就会返回 304 状态码,并且更新浏览器现有缓存有效期
当资源失效时,返回 200 状态码和最新的资源
协商缓存可以通过设置两种 HTTP Header实现
- Last-Modified
- ETag
Last-Modified
浏览器发起请求访问目标资源,服务器在返回资源的同时,会在response header中添加 Last-Modified这个header,表示这个资源在服务器上的最后修改时间
浏览器下一次请求这个资源,浏览器检测到有 Last-Modified这个header,于是会添加If-Modified-Since这个header其值就是Last-Modified中的值
服务器再次收到这个资源请求,会根据 If-Modified-Since 中的值与服务器中这个资源的最后修改时间对比
- 若服务器的资源最后被修改时间不等于于If-Modified-Since中的值的话就会将新的资源发送回来
- 否则返回 304 状态码
缺点:
- 如果本地打开缓存文件,即使没有对文件进行修改,但还是会造成 Last-Modified 被修改,服务端不能命中缓存导致发送相同的资源
- Last-Modified 只能以秒计时,如果在不可感知的时间内修改完成文件,那么服务端会认为资源还是命中了,不会返回正确的资源
因为以上这些弊端,所以在 HTTP / 1.1 出现了 ETag
ETag
Etag是服务器在响应请求时,返回的当前资源文件一个唯一标识(由服务器生成),只要资源有变化,Etag就会重新生成
浏览器在向服务器发送请求时,会将上一次返回的Etag值放到请求头的If-None-Match字段里
服务端比较 If-None-Match 中的值跟目标资源的ETag是否一致
- 一致,响应状态码为304
- 不一致,响应状态码为200,并返回新的资源
特点:
- ETag 优先级比 Last-Modified 高
- ETag 是服务端通过算法计算得出,需要损耗一定时间
两者对比
- Etag/If-None-Match优先级高于Last-Modified/If-Modified-Since
- 即同时存在则只有Etag / If-None-Match生效
- 性能上,Last-Modified要优于Etag
- Last-Modified只需要记录时间
- Etag需要服务器通过算法来计算出一个hash值
- 在精确度上,Etag要优于Last-Modified
- Last-Modified的时间单位是秒,如果某个文件在1秒内被改变了多次,那么它的Last-Modified没有体现出来修改
- 文件只要发生改变,Etag就会改变
执行流程
- 强制缓存优先于协商缓存进行
- 若强制缓存(Expires和Cache-Control)生效则直接使用缓存
- 若不生效则进行协商缓存(Last-Modified / If-Modified-Since与Etag / If-None-Match)
- 协商缓存由服务器决定是否使用缓存
- 若协商缓存失效,那么代表该请求的缓存失效,响应200,返回新的资源和缓存标识,并存入浏览器缓存中
- 生效则响应304,表示继续使用现有缓存
如果什么缓存策略都没设置,那么浏览器会怎么处理:question:
对于这种情况,浏览器会采用一个启发式的算法
取响应头中的 Date 减去 Last-Modified 值的 10% 作为缓存时间
缓存策略应用场景
1. 频繁变动的资源
- 首先使用
Cache-Control: no-cache
,使浏览器每次都请求服务器 - 配合 ETag 或 Last-Modified 来验证资源是否过期
这样的做法虽然不能节省请求数量,但是能显著减少响应数据大小
2. 代码文件
缓存除HTML
外的文件
一般来说,现在都会使用工具来打包代码,那么就可以对HTML引用的静态资源的文件名进行哈希处理,只有当文件内容发生修改后才会生成新的文件名
因此可以给代码文件设置缓存有效期一年Cache-Control: max-age=31536000,这样只有当HTML
文件中引入的文件名发生了改变才会去下载最新的代码文件,否则就一直使用缓存。
参考
原文首发于个人博客,专题系列会整理多篇,与大家一起学习,共同进步,如有错误,还请斧正
浏览器专题系列文章
- 1. 浏览器专题系列 - 缓存机制
- 2. 浏览器专题系列 - 渲染原理
- 3. 浏览器专题系列 - 阻塞渲染
- 4. 浏览器专题系列 - 本地存储
- 5. 浏览器专题系列 - Web安全
- 6. 浏览器专题系列 - 浏览器内核
- 7. 浏览器专题系列 - 跨域与跨站
- 8. 浏览器专题系列 - 事件循环机制