浏览器缓存策略

1,116 阅读6分钟

前言

众所周知,web开发过程中,网页资源响应速度一直是作为网站性能好坏的重要评估标准之一,而缓存技术一直以来在WEB技术体系中都扮演着非常重要的角色,通过缓存技术以及相应的缓存命中策略可以缩短网络请求时间,通过将耗时计算以及耗时传输的结果存起来,在不发生变更的情况下直接复用该结果,从而在保证结果正确性的同时大大提高网站的性能。

在实际WEB开发过程中,缓存技术会涉及到不同层、不同端,比如:用户层、系统层、代理层、前端、后端、服务端等,每一层的缓存目标都是一致的,就是尽快返回请求数据、减少延迟,但每层使用的技术实现是各有不同,面对不同层、不同端的优劣,选用不同的技术来提升系统响应效率。如图展示了各层相关的缓存技术: Untitled.png

如上图所示,浏览器缓存属于用户层的缓存技术,它的主要任务就是一个把已经请求过的web资源(htmljscssimage…)拷贝一份副本到本地,在下一次再次请求该资源时,根据缓存命中机制选择是使用这份副本来直接响应请求,还是向源服务器重新请求最新的资源。

浏览器缓存也是前端开发中最常会接触到的缓存技术,当我们开发了最新的版本之后,需要将打包结果也就是静态资源(html,js,css)部署到服务器上。此时因为浏览器缓存,可能存在即使已经在服务器上更新了资源,但是用户进入网站时并不会看到最新的版本,这里就需要我们了解浏览器的缓存,从而避免这种情况的发生;同时还要利用缓存技术,缓存不常更新的静态资源,提高用户打开网页的速度。

接下来就详细的了解一下浏览器缓存。

查看浏览器缓存

缓存对象

首先浏览器缓存的对象是一次HTTP响应报文的整体,包括响应行,响应头和响应体。其中,常见的HTTP请求方法中只能存储GET响应。通过在firefoxabout:cache我们可以查看到浏览器缓存的HTTP响应:

打开baidu.com,查看network控制面板,找到任意一个js资源,记住资源名: Untitled 1.png

查看firefox缓存: Untitled 2.png

disk cache的所有缓存内容列表: Untitled 3.png

找到刚才的js资源名,打开查看缓存内容: Untitled 4.png

但是并不是所有的GET响应都会被浏览器缓存,只要状态码为以下这些时,响应才会被缓存:

  • 200:一个检索请求的成功响应
    • HTML文档、js、css、图片、字体文件等
  • 301:永久重定向
  • 404:错误响应
  • 206:不完全响应

资源缓存位置

浏览器获取缓存的顺序为 Service Worker Cache、Memory Cache、Disk Cache、(至于 Push Cache 属于 HTTP2 待验证)。

  • from memory cache:是把资源存到内存中,当进程退出时(关闭浏览器),内存中的数据会清空,下次访问要执行别的缓存策略。
  • from disk cache:是把资源缓存在磁盘中,进程退出时不受影响,下次访问可以继续执行此次缓存策略。
  • Sevice Worker Cache(https):开发者人为存储的永久性存储,用于离线缓存的处理。Application -> Cache Storage 查看。
  • Push Cache(http2):Push Cache 是 HTTP2 在 sever push 阶段存在的缓存。

浏览器缓存策略

缓存命中机制主要分为两个阶段:强缓存协商缓存。其中无论是哪种缓存命中,最终使用的都是浏览器缓存到本地的资源。区别在于强缓存不发生网络请求。

强缓存主要是浏览器自行判断资源是否过期,如果不过期则直接使用缓存的资源(强缓存命中),不再进行网络请求;

协商缓存则是在强缓存阶段无法命中的情况下,浏览器发起请求,询问服务器是否可以使用本地缓存资源,如果服务器检查发现浏览器本地的资源没有过期,则返回304告诉浏览器可以使用本地的缓存资源(协商缓存命中);

如果强缓存和协商缓存都没有命中的情况下,服务器会返回最新的资源,浏览器拿到资源后会更新缓存的资源信息。

当前资源是否能被缓存以及是否能通过浏览器缓存机制命中(浏览器是否启动缓存),主要是服务器来设置。当该资源首次被请求时,**服务器通过设置HTTP响应的响应头来设置该资源的缓存信息。**主要是以下几个字段:

  • Expires
  • cache-control
  • Etag
  • Last-Modified

强缓存

强缓存是利用Expires或者Cache-Control这两个http response header实现的,它们都用来表示资源在客户端缓存的有效期。

强缓存就是直接由浏览器判断缓存是否命中,不经过网络。当浏览器发起HTTP请求前,首先会查看当前的缓存列表,找到该资源的响应头,拿到Cache-Control或者Expires字段,通过对两个字段判断强缓存是否命中。当强缓存命中时,HTTP状态码为200,资源从缓存中加载( from memory cache / from disk cache)。

优先级:其中Cache-Control 的优先级大于Expires ,其实还有个Pragma 字段,它的优先级最高,但是不常用。

通过控制台Network 面板查看:

Untitled 5.png

Expires (优先级最低)

  • HTTP 1.0 用于缓存管理的header字段,由服务器返回,用GMT格式的字符串表示。
  • 值表示一个资源过期的时间,描述的是绝对时间,且该绝对时间属于服务端的时间系统。
  • 判断方法:浏览器发起下一次请求时,当前HTTP发起的请求时间 (this http request time) < (expires设置的值),资源没有过期,缓存命中。
  • 弊端:Expires遵循的是服务端的时间系统,而请求时间遵循的是客户端的时间系统,如果两者时间不是一致的,就可能产生误差。例如手动修改本地客户端的时间,那么就可能影响缓存命中结果。

Cache-Control(优先级第二)

为了解决Expires因为客户端和服务器端时间不统一带来的问题,HTTP 1.1 提出了新的header 也就是 Cache-Control ,这个字段使用相对时间,进行比较的时候用的都是客户端的时间,相对来更有效与安全。

  • HTTP 1.1
  • 值是一个相对时间,以秒为单位,用数值表示
  • 判断方法:浏览器发起下一次请求时,当前HTTP发起的请求时间(this http request time) < (last http request time + cache-control 设置的值) ,资源没有过期,缓存命中。
Cache-Control: public, max-age=31536000

header字段的其他取值如下:

字段名位置说明
no-cache请求头,响应头强制客户端向服务器发送请求(禁止强缓存)。这个值不是禁止客户端或者代理服务器缓存响应。
no-store请求头,响应头禁止一切缓存。客户端和代理服务器都不能缓存响应。
max-age请求头,响应头设置资源(representations)可以被缓存多长时间,单位是秒。
no-transform请求头,响应头代理不可更改媒体类型
cache-extension请求头,响应头新指令标记(token)
s-maxage响应头和max-age同理,只不过是针对代理服务器缓存而言。
private响应头不能被代理服务器缓存
public响应头响应可以被任何缓存区缓存
must-revalidate响应头在缓存过期前可以使用,缓存过期以后必须向服务器验证。
proxy-revalidate响应头要求中间缓存服务器对缓存的响应有效性需再次确认(代理服务器需要发送请求给服务器端确认资源有效性,不能直接返回缓存)
only-if-cached请求头从缓存中获取资源
min-fresh请求头单位:秒,期望在指定的时间内,响应仍有效
max-stale请求头单位:秒, 接受已过期的响应

Pragma (优先级最高)

除了 ExpiresCache-Control以外,还有Pragma字段,但是这个字段用得很少,它只有一个值就是 no-cache,含义等同于Cache-Control 取值为 no-cache,表示禁止强缓存,强制客户端发送http请求给客户端。

协商缓存

协商缓存是指,当强缓存没有命中的情况下,浏览器会发送http请求给服务端,此时服务端并不会直接处理请求,而是会再进行一次缓存命中判断,这个就是协商缓存。如果缓存命中成功,则会返回一个304的状态码,告诉浏览器当前资源没有过期。浏览器接收到304响应后,就会使用本地缓存的响应。

304响应是一个只有响应头,响应体为空的响应。

Untitled 6.png

Last-Modified / If-Modified-Since

  • 该字段是资源最后的修改时间。
  • 浏览器发送请求时,会将上次响应头中的 Last-Modified 赋值给 本次请求头中的 If-Modified-Since 字段。服务端中接收到请求之后,会将这个字段和当前资源最后的修改时间做对比,
    • 如果 If-Modified-Since (上一次资源修改时间) < 服务器上资源的最后修改时间,则说明当前资源被修改过了,服务端需要返回新的资源给服务端,此时响应200,返回正常的响应。同时这次响应会返回新的Last-Modified 值,用于更新浏览器缓存。
    • 如果 If-Modified-Since (上一次资源修改时间)≥ 服务器上资源的最后修改时间,则说明没有修改过资源,则返回304状态码,不会返回资源内容。
  • 弊端:
    • 短时间内资源发生了变化,这个字段并不会发生变化,缓存命中可能失效。
    • 如果出现了服务器资源因为反复修改,但资源内容并没发生变化,此时浏览器再次请求服务器,实际上应该认为缓存命中(实际内容没有变化),但是此时通过该字段的比较会导致缓存命中失效。

Etag / If-None-Match (优先级最高)

  • 为了解决Last-Modifed的缓存命中问题,可以通过Etag来管理协商缓存命中。
  • 该字段是当前资源在服务器的唯一标识(生成规则有服务器决定),是基于文件内容进行编码的,如果文件内容不发生变动,那么该标识不会发生变更。
  • 服务端收到响应以后,根据当前资源内容重新生成一份Etag ,比较该值和If-None-Match 是否相等,相等则返回304,不相等则返回200和正常响应。但同Last-Modified 的区别在于即使服务器重新生成的Etag字段和原来的没有变化,但是因为重新生成了,304响应中同样会返回Etag字段。

缓存流程图示

缓存流程图示.jpg

用户操作执行的缓存策略

一些涉及到缓存判断的用户行为操作

行为可以使用的缓存策略说明
在URL输入栏中输入然后回车 / 访问书签强缓存,协商缓存
地址栏回车 / 正常重新加载(command + r)协商缓存在请求头加入 Cache-Control: max-age=0 使强缓存无效
F5 / 点击工具栏刷新按钮 / 右键菜单重新加载(command + r)协商缓存在请求头加入 Cache-Control: max-age=0 使强缓存无效
ctrl + F5 (command + shift + r)协商缓存在请求头加入Cache-Control: no-cache 使强缓存无效
用户刷新

参考