Skip to main content

浏览器缓存机制

缓存的作用

加快页面响应,减少请求次数,提高性能,降低服务器压力。

缓存位置

memory cache

Memory Cache 也就是内存中的缓存,读取内存中的数据肯定比磁盘快。但是内存缓存虽然读取高效,可是缓存持续性很短,会随着进程的释放而释放。 一旦我们关闭 Tab 页面,内存中的缓存也就被释放了。

那么既然内存缓存这么高效,我们是不是能让数据都存放在内存中呢?

先说结论,这是不可能的。首先计算机中的内存一定比硬盘容量小得多,操作系统需要精打细算内存的使用,所以能让我们使用的内存必然不多。内存中其实可以存储大部分的文件,比如说 JS、HTML、CSS、图片等等。

当然,我通过一些实践和猜测也得出了一些结论:

  • 对于大文件来说,大概率是不存储在内存中的,反之优先
  • 当前系统内存使用率高的话,文件优先存储进硬盘

disk cache

Disk Cache 也就是存储在硬盘中的缓存,读取速度慢点,但是什么都能存储到磁盘中,比之 Memory Cache 胜在容量和存储时效性上。

在所有浏览器缓存中,Disk Cache 覆盖面基本是最大的。它会根据 HTTP Herder 中的字段判断哪些资源需要缓存并且即使在跨站点的情况下,相同地址的资源一旦被硬盘缓存下来,就不会再次去请求数据。

缓存策略

强缓存

一旦命中,不会再向服务器发送请求

Expires - 实体首部

值是一个 GMT 时间,http1.0 中与缓存相关的头部。请求的资源在这个时间之内都是有效的。

缺点:依赖于本地时间,而本地时间是可修改的

Cache-Control - 通用首部

http1.1

max-age: 秒

是一个相对时间,单位是 s,优先级高于 Expires。

  • public:响应可以被客户端和代理缓存
  • private:响应只能被客户端缓存
  • max-age: 单位 s,有效时间
  • no-store:资源不可以被缓存,每次都重新请求
  • no-cache:资源可以被缓存,但是不能直接使用,每次都需要发送请求验证有效性
  • max-stale:单位 s,表示可以接受一个过期的资源,但是过期的时间不能超过这个字段的限制 - 请求
  • max-fresh:单位 s,希望请求的资源在这个时间内都是有效的 - 请求

协商缓存

只有在强缓存失效的情况下才会使用协商缓存,协商缓存会发送请求到服务器验证资源的有效性,如果有效的话则服务器返回状态码 304 并且不返回任何内容,浏览器则直接使用缓存;失效则返回 200 以及相关的响应内容和与缓存相关的首部

Last-Modified - 实体首部

http1.0

一个 GMT 格式的时间。表示资源的最后修改时间

Last-Modified/If-Modified-Since

缺点:

  1. 只能精确到 s,但是资源可能会在 1s 内被修改了多次,导致客户端拿不到最新资源
  2. 如果本地打开了缓存的文件,即使没有对文件进行修改,还是会导致 Last-Modified 发生被变化,导致缓存失效

Etag

http1.1

服务器根据所请求的资源内容生成的唯一标识

优先级高于 Last-Modified

Etag/If-None-Match

info

如果什么缓存策略都没设置,那么浏览器会怎么处理?

对于这种情况,浏览器会采用一个启发式的算法,通常会取响应头中的 Date 减去 Last-Modified 值的 10% 作为缓存时间。

用户操作对缓存的影响

  • 地址栏回车、链接跳转、新开窗口

协商缓存/强缓存都有效

  • 刷新

协商缓存有效,强缓存失效

浏览器会在请求头里加一个“Cache-Control: max-age=0” 强行发起请求,它可以配合 ETag 和 Last-Modified 使用,如果本地缓存还在,且服务器返回 304 ,依然可以使用本地缓存。

  • 强制刷新

协商缓存和强缓存都失效

发送的请求的请求头中均携带 Cache-Control: no-cache(为了兼容,还携带了 Pragma: no-cache),服务器直接返回 200 和最新的资源

实际场景应用缓存策略

频繁变动的资源

对于频繁变动的资源,首先需要使用 Cache-Control: no-cache 使浏览器每次都请求服务器,然后配合 ETag 或者 Last-Modified 来验证资源是否有效。这样的做法虽然不能节省请求数量,但是能显著减少响应数据大小。

代码文件

这里特指除了 HTML 外的代码文件,因为 HTML 文件一般不缓存或者缓存时间很短。

一般来说,现在都会使用工具来打包代码,那么我们就可以对文件名进行哈希处理,只有当代码修改后才会生成新的文件名。基于此,我们就可以给代码文件设置缓存有效期一年 Cache-Control: max-age=31536000,这样只有当 HTML 文件中引入的文件名发生了改变才会去下载最新的代码文件,否则就一直使用缓存