【浏览器工作原理】04 - 浏览器缓存

353 阅读5分钟

首先我们需要知道的是浏览器缓存也叫做 HTTP 缓存,分为强制缓存协商缓存

优先级较高的是强制缓存,命中强制缓存失败的情况下才会走协商缓存

强制缓存

强制缓存是利用 HTTP 头中的 ExpiresCache-Control 两个字段来控制的。

  • Expires —— HTTP 的日期或者时间戳,告诉浏览器缓存有效性持续到设置的时间为止,在这个时间之内相同的请求使用缓存,这个时间之外使用http请求

    eg:Expires: Wed, 21 Oct 2015 07:28:00 GMT

  • Cache-Control —— 被用于在http请求和响应中,通过指定指令来实现缓存机制。缓存指令是单向的,这意味着在请求中设置的指令,不一定被包含在响应中。

浏览器会根据这两个字段判断目标资源是否命中强制缓存,如果命中则直接从缓存中获取资源,不会再与服务器发生通信。

比如说设置了 Expires 之后,如果本地时间小于 Expires 设置的过期时间,就会直接从缓存中取这个资源。由于是本地时间和服务器设置的时间作对比,所以我们也不难发现使用 Expires 的缺陷:

Expires 对本地时间依赖,如果服务器和客户端的时间设置不同,或者用户手动修改了客户端的时间,那么 Expires 可能无法达到我们的预期。

由于 Expires 的局限性,HTTP 1.1 新增了 Cache-Control 来完成 Expires 的任务,简而言之,Expires 可以做的 Cache-Control 都可以做,并且新增了一些 Expires 做不到的功能,所以 Cache-Control 完全可以视为 是Expires 完全替代方案。在当下的前端实践里,我们继续使用 Expires 的唯一目的就是向下兼容。

Cache-Control 中,我们通过 max-age 来控制资源的有效期,它不是一个时间戳,而是一个时间长度,🌰:

cache-control: max-age=31536000

在这个例子中,max-age 设置为 31536000,意味着这个资源在 31536000秒 内都是有效的,这就完美的避开了时间戳带来的潜在问题。

Cache-Control 相对于 expires 更加准确,它的优先级也更高。当 Cache-Control 与 expires 同时出现时,我们以 Cache-Control 为准。

协商缓存

协商缓存依赖于服务端与浏览器之间的通信,在协商缓存的机制下,浏览器需要向服务器询问缓存的相关信息,进而判断是重新发起请求下载完整的响应,还是从本地获取缓存的资源。

如果服务器提示 Not-Modified 表示 资源未改动,则资源会被重定向到浏览器缓存(状态码 304)

而协商缓存的实现,从 Last-ModifiedEtag

  • Last-Modified —— 是一个时间戳,如果我们启用了协商缓存,那么它会在首次请求的时候随着 Response Headers 返回

    🌰:Last-Modified: Fri, 19 Mar 2021 06:13:09 GMT
    

    随后我们每次请求时,都会在 request header 中带上一个 If-Modified-Since 的时间戳字段,它的值就是上一次 response 返回的 last-modified 的值

    If-Modified-Since: Fri, 19 Mar 2021 06:13:09 GMT
    

    服务器接收到这个时间戳后,会对比改时间戳在服务器上的最后修改时间是否一致,从而判断资源是否发生了变化,如果发生了变化就返回一个完整的响应内容,并且在 Last-Modified 中返回最新的修改时间,否则 返回 304,重定向到浏览器缓存。且之后的 response header 不会再返回 Last-Modified 字段。

    而使用 Last-Modified 其实有一些弊端,最常见的两个场景就是:

    • 我们编辑了一个文件,但是实际的内容没有改变,服务器不知道我是不是真的改了文件,它依然是通过最后的修改时间来判断的,这样这个资源再次被请求的时候也会被当做新资源,进而引发一次完整的响应(浪费流量,不该重新请求的时候也重新请求了)
    • 当我们修改文件的速度太快了时候(比如 100ms 就完成了改动),而 If-Modified-Since 只能检查到 以秒为最小计量单位的时间差,所以这种改动它是感知不到的(该重新请求的时候反而没有重新请求了)

    所以这两个场景其实是同一个 bug —— 服务器并没有正确感知文件的变化,So,和强制缓存一样,有问题就有解决方案。Etag 应运而生

  • Etag —— Etag 是作为 Last-Modified 的补充出现的,它是由服务器为每一个资源生成的唯一标识符,这个标识符可以试基于文件内容编码的,只要文件内容不同,生成的 Etag 就是不同的。所以 Etag 可以精准的感知文件的变化。

    但是 Etag 生成的过程需要服务器额外付出开销,影响性能,所以启用Etag 的时候需要权衡考虑。它不能代替 Last-Modified,只能作为 Last-Modified的补充和强化存在。

Etag 在感知文件的变化上比 Last-Modified 更加准确,优先级也更高,当 Etag 和 Last-Modified同时存在的时候,以 Etag 为准。