整理一些前端必须了解的网络知识(一)——缓存

436 阅读6分钟

浏览器缓存机制的关键:浏览器每次发起请求,都会先在浏览器缓存中查找该请求的结果以及缓存标识。并且在每次拿到返回的请求结果都会将该结果和缓存标识存入浏览器缓存中。

那么缓存具体如何存储和使用?

缓存位置

首先介绍一下内存缓存(from memory cache)和硬盘缓存(from disk cache),如下:

  • 内存缓存(from memory cache):内存缓存具有两个特点,分别是快速读取和时效性
    • 快速读取:内存缓存会将编译解析后的文件,直接存入该进程的内存中,占据该进程一定的内存资源,以方便下次运行使用时的快速读取
    • 时效性:一旦该进程关闭,则该进程的内存则会清空
  • 硬盘缓存(from disk cache):硬盘缓存则是直接将缓存写入硬盘文件中,读取缓存需要对该缓存存放的硬盘文件进行I/O操作,然后重新解析该缓存内容,读取复杂,速度比内存缓存慢
  • 浏览器读取缓存的顺序为memory –> disk

浏览器会在js和图片等文件解析执行后直接存入内存缓存中,那么当刷新页面时只需直接从内存缓存中读取(from memory cache);而css文件则会存入硬盘文件中,所以每次渲染页面都需要从硬盘读取缓存(from disk cache)。

举个例子区分from memory cache 和 from disk cache:

访问一个网页 –> 200 –> 关闭标签页 –> 重新打开该网页 –> 200(from disk cache) –> 刷新 –> 200(from memory cache)

缓存使用策略

通常浏览器缓存策略分为两种:强缓存协商缓存,并且缓存策略都是通过设置 HTTP Header 来实现的。

  • 强缓优先于协商缓存
  • 不存在该缓存结果和缓存标识,强制缓存失效,则直接向服务器发起请求
  • 存在该缓存结果和缓存标识,该结果尚未失效,强制缓存生效
  • 存在该缓存结果和缓存标识,但该结果已失效,强制缓存失效,则使用协商缓存

强缓存

控制强制缓存的字段分别是ExpiresCache-Control,其中Cache-Control优先级比Expires高。

Cache-Control

在HTTP/1.1中,Cache-Control是最重要的规则,主要用于控制网页缓存,主要取值为:

public:所有内容都将被缓存(客户端和代理服务器都可缓存)
private:所有内容只有客户端可以缓存,Cache-Control的默认取值
no-cache:虽然字面意义是“不要缓存”。但它实际上的机制是,仍然对资源使用缓存,但每一次在使用缓存之前必须(MUST)向服务器对缓存资源进行验证
no-store:所有内容都不会被缓存,即不使用强制缓存,也不使用协商缓存
max-age=xxx (xxx is numeric):缓存内容将在xxx秒后失效

Expires

Expires是HTTP/1.0控制网页缓存的字段,其值为服务器返回该请求结果缓存的到期时间,即再次发起该请求时,如果客户端的时间小于Expires的值时,直接使用缓存结果。

注意:

现在浏览器默认使用的是HTTP/1.1,Expire已经被Cache-Control替代,原因在于Expires控制缓存的原理是使用客户端的时间与服务端返回的时间做对比,那么如果客户端与服务端的时间因为某些原因(例如时区不同;客户端和服务端有一方的时间不准确)发生误差,那么强制缓存则会直接失效。

协商缓存

控制协商缓存的字段分别有:Last-Modified / If-Modified-SinceEtag / If-None-Match,其中Etag / If-None-Match的优先级比Last-Modified / If-Modified-Since高。

Last-Modified / If-Modified-Since

Last-Modified 是服务器响应请求时,返回该资源文件在服务器最后被修改的时间。

If-Modified-Since 则是客户端再次发起该请求时,携带上次请求返回的Last-Modified值,服务器将该值与该资源在服务器的最后被修改时间做对比,若资源有更新,则重新返回资源,状态码为200;否则则返回304,代表资源无更新,可继续使用缓存文件。

Etag / If-Modified-Since

Etag 是服务器响应请求时,返回当前资源文件的一个唯一标识(由服务器生成)。

If-None-Match 是客户端再次发起该请求时,携带上次请求返回的唯一标识Etag值,由服务器对比后决定是否使用缓存。

比较

缓存分类缓存标识特点
强缓存Cache-Control1、解决了Expires服务器和客户端相对时间的问题。
2、比Expires多了很多选项设置。
3、存在版本问题,到期之前的修改客户端是不可知的。
强缓存Expires1、服务器和客户端可能存在时间误差。
2、存在版本问题,到期之前的修改客户端是不可知的。
协商缓存Last-Modified / If-Modified-Since1、不存在版本问题,每次请求都会去服务器进行校验。
2、但只要资源修改,无论内容是否发生实质性的变化,都会将该资源返回客户端。
3、以时刻作为标识,无法识别一秒内进行多次修改的情况。 如果资源更新的速度是秒以下单位,那么该缓存是不能被使用的,因为它的时间单位最低是秒。
4、某些服务器不能精确的得到文件的最后修改时间。
5、如果文件是通过服务器动态生成的,那么该方法的更新时间永远是生成的时间,尽管文件可能没有变化,所以起不到缓存的作用。
协商缓存Etag / If-Modified-Since1、可以更加精确的判断资源是否被修改,可以识别一秒内多次修改的情况。
2、不存在版本问题,每次请求都回去服务器进行校验。
3、计算ETag值需要性能损耗。
4、分布式服务器存储的情况下,计算ETag的算法如果不一样,会导致浏览器从一台服务器上获得页面内容后到另外一台服务器上进行验证时现ETag不匹配的情况。

另外,命中强缓存,缓存过的数据是从本地cache直接返回缓存数据,不会发起http请求;协商缓存则是会向服务端发起http请求,返回的数据只会有header,没有body的数据包。

应用场景分析

  1. 频繁变动的资源 Cache-Control: no-cache 配合 ETag 或者 Last-Modified 来验证资源是否有效。这样的做法虽然不能节省请求数量,但是能显著减少响应数据大小。

  2. 不常变化的资源 Cache-Control: max-age=31536000 很大的 max-age 强制缓存。而为了解决更新的问题,就需要在文件名(或者路径)中添加 hash, 版本号等动态字符,重新请求资源并缓存。

参考

  1. 【第1250期】彻底理解浏览器的缓存机制
  2. 深入理解浏览器的缓存机制
  3. 说一下 Http 缓存策略,有什么区别,分别解决了什么问题