更清晰的了解浏览器缓存机制

150 阅读4分钟

以前学习浏览器缓存的机制,都是通过记忆http报文字段,后来发现这样记忆方法很笨,今天重新梳理了下流程。

为何要缓存

首先,我们知道缓存是为了节省流量、加快网页加载速度、减少服务器压力的一种机制。就是把使用过的一些资源文件存在本地,方便再次使用。

如何缓存

  1. 客户端第一次访问服务器,加载并缓存了一部分资源
  2. 客户端第二次访问服务器,发现有缓存,查看缓存是否过期,进入强缓存阶段

强缓存

这一阶段主要是看两个字段:

Cache-Control

主要是看max-age(单位-秒)是否过期,这个是简单的时间对比,把第一次缓存的时间跟当前时间对比,这里有两种情况:

  • 如果还在有效期,就直接使用缓存,不做其他判定了
  • 如果过期了,就需要重新去判定缓存是否可用,原因是:时效期过了并不代表文件有改动(max-age值为100,但其实文件一直都没改动),会进入协商缓存阶段。

Expires

Expires是HTTP/1.0控制网页缓存的字段,在1.1版本中被Cache-Control替代了,二者同时存在,取Cache-Control。

Expires也是做失效对比的字段:Expires控制缓存的原理是使用客户端的时间与服务端返回的时间做对比,但他有明显的缺点,如果服务器和客户端不在同一时区(例如美国和中国),那么对比就没有意义了,所以后来废除了。他的时效期对比也是和Cache-Control同样的逻辑。

协商缓存

缓存文件时效期过了,进入协商缓存阶段。协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程。

这一阶段也是看两个字段:

Last-Modified / If-Modified-Since

这个字段主要是用于判定文件修改的时间。

image.png

我们第一次缓存文件的时候,会储存响应头中的Last-Modified字段,在缓存时效期过了之后,会带上If-Modified-Since(值为缓存下来的Last-Modified)字段去请求服务器的文件:

服务器发现请求头中带有If-Modified-Since,那么就会取对比文件的Last-Modified:

  • Last-Modified / If-Modified-Since相等,代表文件没有改动,返回304状态码,客户端收到后就继续使用缓存文件
  • 如果值不一致,则文件代表有改动,服务器就会返回改动后的资源文件,客户端会重新加载文件,然后缓存下来

image.png

Etag / If-None-Match

image.png

跟前一个字段一样,只是优先级高于Last-Modified(同时存在取Etag)。

这个字段主要是用于判断文件版本,相当于给当前文件打上一个标记,当文件修改之后,标记也会变,没有改动则不变。

我们第一次缓存文件的时候,会储存响应头中的Etag字段,在缓存时效期过了之后,会在请求头带上If-None-Match(值为缓存下来的Etag)字段去请求服务器的文件:

image.png 服务器发现请求头中带有If-None-Match,那么就会取对比文件的Etag:

  • Etag / If-None-Match相等,代表文件没有改动,返回304状态码,客户端收到后就继续使用缓存文件
  • 如果值不一致,则文件代表有改动,服务器就会返回改动后的资源文件,客户端会重新加载文件,然后缓存下来

总结

强缓存优先级是高于协商缓存的,主要是通过Cache-Control和Expires做简单的时间对比。

协商缓存主要是通过客户端带上标记去询问服务器,看缓存是否过期,服务器通过(Etag / If-None-Match)和(Last-Modified / If-Modified-Since)做类似文件版本的对比。

判断缓存的伪代码

访问服务器--开始

if (有缓存) {
    if (Cache-Control的max-age时效过期了) {
        // 缓存是过期了,但是万一服务器的文件没有改动呢,那不就可以继续用缓存啦,就执行下面逻辑
        if (缓存文件的If-None-Match === 服务器文件的Etag) {
            缓存还可以继续用,使用缓存
        } else if (缓存文件的If-Modified-Since === 服务器文件的Last-Modified) {
            // If-None-Match/Etag 优先级高于 If-Modified-Since/Last-Modified
            缓存还可以继续用,使用缓存
        } else {
            文件被修改过啦,缓存没办法用了,重新请求资源文件
        }
    } else if (Expires的时效过期了) {
        // Cache-Control的优先级高于Expires
        文件被修改过啦,缓存没办法用了,重新请求资源文件
    } else {
        缓存还能用,直接使用
    }
} else {
    没有缓存,请求资源文件
}