我们知道通过HTTP进行请求的时候是十分占用资源的,需要进行三次握手建立连接,而且对于大数据的加载是十分缓慢的。对于用户以及开发者来说,我们希望实现对于变动不大的数据进行缓存复用,提升数据加载速度,缓解服务器压力。
缓存机制
首先我们看下下面两种场景下的网络请求状态。
场景一:页面的前进后退场景
场景二:页面按F5刷新
我们来看下网络状态,大概可以分为下面几种状态。
- memory cache:内存缓存,存在浏览器的进程里面一般包括图片和js
- disk cache: 硬盘缓存,存在浏览器本地。
- 返回码304: 文件没有发生变化(服务端返回)
- 真正的网络请求:进行http网络请求,一般是缓存过期,或者没有缓存,或者不适用缓存等场景。
可以看出来,网络请求存在上述缓存状态,后面我们会解析这些缓存的逻辑和实现机制。
1.1 HTTP头信息
我们来看一下这些请求的请求头和响应头存在什么差异。
其中涉及到如下HTTP首部
- 通用头信息
字段 | 描述 |
---|---|
Cache-Control | 缓存控制,具体取值和作用参考下面表格 |
Pragma | 另一种随报文传输的指示方式,并不专用于缓存,当该字段值为no-cache的时候,会客户端不要对该资源读缓存 |
Cache-Control取值范围
字段 | 描述 |
---|---|
private | 仅客户端可以缓存,代理层不可以访问 |
public | 客户端和代理服务器都可缓存 |
max-age=xxx | 缓存的内容将在 xxx 秒后失效 |
no-cache | 需要使用对比缓存来验证缓存数据 |
no-store | 所有内容都不会缓存,强制缓存,对比缓存都不会触发 |
- 请求头:
字段 | 描述 |
---|---|
If-Match | 比较ETag是否一致 |
If-None-Match | 比较ETag是否不一致 |
If-Modified-Since | 比较资源最后更新的时间是否一致 |
If-Unmodified-Since | 比较资源最后更新的时间是否不一致 |
- 实体头信息:
字段 | 描述 |
---|---|
ETag | 资源的唯一标识信息,资源发生变化会生成新的 |
Expires | 有效期时间,服务端返回的到期时间 |
Last-Modified | 最后一次修改的时间 |
1.2 浏览器缓存逻辑
缓存整体逻辑如下:
- 第一步,先判断是否存在缓存,当存在缓存的时候,判断缓存是否过期。这里需要用到的是缓存响应头里面的Expires和Cache-Control。
- 会优先读取Cache-Control的值,如果存在max-age,将用报文生成时间Date+max-age 和当前时间对比,参看是否过期。如果明确为no-cache,则会不使用缓存,直接请求网络。no-store网络请求结果不会存储在本地,不会产生缓存。
- 如果上述Cache-Control值。比较Expires(HTTP1.0的遗留物)中的过期时间与当前时间。判断是否过期。
- 第二步,如果缓存没有过期,判断上一次缓存结果是否存在ETag和Last-Modified。如果没有直接进行HTTP请求,如果有则进入下一步。因为这两个值是用于服务器判断缓存是否可用(也就是服务端文件是否发生变化的)
- 第三步,请求头中携带If-None-Match(缓存实体ETag) 和 If-Modified-Since(缓存时间)。主要有以下判断逻辑:
- If-None-Match 携带的是缓存实体的Etag,到达服务端后,服务端首先会判断当前的ETag跟If-None-Match 是否匹配,如果匹配则返回304,浏览器则会读取硬盘缓存,并显示;如果不匹配,则将服务端的实体返回,并将新的ETag写入响应头。
- If-Modified-Since 值为缓存实体里面响应头的Last-Modified 字段,请求头携带这个值带到服务端,服务端会判断当前实体在这个时间后时候发生了变化,如果发生了变化,则将新的请求结果返回,否则返回304,浏览器会使用缓存。
- 如果上述缓存都没有命中,则需要将最新的实体结果通过网络返回浏览器,包括响应头信息。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END