写在前面
HTTP 缓存是浏览器缓存的一种,浏览器缓存主要是HTTP协议定义的缓存机制,设置 HTTP 缓存相关信息的有 HTML meta 标签设置、HTTP 头信息控制等,但一般应用广泛的还是用 HTTP 头消息控制缓存。
关于缓存方式的介绍,还是从缓存的大体分类展开,这样便于比较以及更好地看清各缓存方法的改进。
为什么需要缓存
通过浏览器输入 url 向服务器发送请求得到响应页面并展示给用户的过程很缓慢,对于一些大的响应需要在客户端和服务端进行多次通信,这大大拖延了浏览器可以使用和处理的时间,因此,缓存和重用之前获得的资源成为性能优化的很关键的一个方向。利用好浏览器的缓存机制,可以大大提高浏览器的处理和显示的效率,用户体验也会更好。
1 强缓存
强缓存就是指服务器在返回资源给浏览器时明确告知浏览器可以在满足缓存条件的情况下直接从本地缓存加载资源,而不必再次从浏览器端获取资源。相当于服务器授予了浏览器可以直接使用本地缓存的权限。
1.1 Expires(HTTP 1.0)
Expires 是响应头 Response Headers 中的一个属性,其属性值是绝对时间,表示该资源的具体过期时间,如Expires:Thu,31 Dec 2037 23:59:59 GMT,这个时间代表着这个资源的失效时间,也就是说在2037年12月31日23点59分59秒之前都是有效的。在资源有效时间内,浏览器再次访问该资源时会命中强缓存并直接从本地缓存加载资源。
但是,使用 Expires 设置的缓存存在一个很大的问题, 由于Expires 的值是一个绝对的时间点,当用户修改了本地时间后,会导致缓存混乱,会让缓存提前过期或者延迟过期。
1.2 Cache-Control(HTTP 1.1)
Cache-Control 也是响应头 Response Headers 中的一个属性,其属性值比较复杂,常用的是 max-age,值是一个时间长度,单位是 s,用于表示资源的有效时长,是一个相对时间。例如 Cache-Control:max-age=31536000,也就是说缓存有效期为(31536000 / 24 / 60 60)天,在 (31536000 / 24 / 60 60)天后该资源就过期不可使用了,相比于 Expires ,Cache-Control 是一个相对时间段,用户改变本地时间,不会影响资源的过期时间。
no-cache 值不是表示不使用缓存,而是让 Cache-Control 缓存方式变成了类似协商缓存的方式,在使用缓存前需要向服务器端询问。
no-store 值表示禁止缓存,每次请求必须从服务器端重新获取。
Cache-Control 和 Expires 可同时存在,同时存在时,Cache-Control 的优先级更高。
2 协商缓存
协商缓存就是指服务器在返回资源给浏览器时没有直接赋予浏览器直接使用本地缓存的权限,因此,浏览器在使用本地缓存之前需要先询问服务器端,由服务器端根据某些条件判断是否允许浏览器使用本地缓存,当服务器端允许浏览器使用本地缓存时就直接返回 304 未修改状态码,告知浏览器使用本地缓存的资源。若不允许浏览器使用缓存时,服务器会返回新的资源给浏览器。
2.1 Last-Modified(HTTP 1.0)
Last-Modified 是响应头 Response Headers 中的一个属性,是服务器端返回的,其值是该资源上次修改的时间点,与浏览器发送的请求头 Request Headers 中的 If-Modified-Since 属性相对应。
浏览器在第一次请求资源时,会将收到的资源响应头里的 Last-Modified 属性的值,即该资源的上次修改时间赋值给下次请求该资源时的请求头的 If-Modified-Since 属性中,让服务端比较该资源的 Last-Modified 和请求头里的 If-Modified-Since 两次的时间是否相同,若相同,表示资源并未修改过,则服务器会返回空和 304 状态码,浏览器调用本地缓存,若两次时间不同,则服务器会返回新的资源。
但是 Last-Modified 的缓存方式存在一些问题,因为 Last-Modified 是只能精确到秒级,若资源在 1s 内更新了资源,则 Last-Modified 属性值不会改变,则会出现误差。又或者一个资源设置了定期生成或刷新,因此当资源内容未变化时,因为定期生成或刷新而改变了 Last_Modified 的值,则浏览器会重新返回资源,导致本地缓存无法使用。
2.2 ETag(HTTP 1.1)
Etag 也是响应头 Response Headers 中的一个属性,是服务器端返回的,其值是一个随机生成的校验码(ETag: entity tag),用于保证每个资源是唯一的,当资源改变时其 ETag的值会改变。 与之对应的是浏览器发送的请求头 Request Headers 中的 If-None-Match 属性。其协商原理和 Last-Modified 类似,Request 中的 If-None-Match 属性用于记录缓存资源的 ETag 值,在再次请求时发送过去在服务器端和资源的当前 ETag 比较,来决定是否命中协商缓存。
Etag 和 Last-Modified 极其相似,但其解决了 Last=Modified 的缺点,资源内容一旦改变,其 ETag 值就会改变,相当于用唯一标记记录资源的改变。是目前最准确的协商缓存方式。
Last-Modified 和 Etag 也可同时存在,二者同时存在时,Etag 的优先级更高。
ETag 中有强 ETag 和 弱 ETag 值之分。
强 ETag 值
不论实体发生多么细微的变化都会改变 ETag 的值。
ETag: "usagi-1234"
弱 ETag 值
只有资源发生了根本性改变,产生差异时才会改变 ETag 的值。这事,会在字段值最开始处附加 W
ETag: W/"usagi-1234"
3 PWA 缓存策略
PWA是最新出来的缓存策略,
4 浏览器加载页面流程
若是浏览器第一次请求某个页面,那么浏览器会直接向服务器发送请求,服务器端处理请求后返回资源,并在响应头告知缓存的权限和方式,浏览器接收资源并进行本地缓存。
当浏览器再次请求某个页面时,会根据资源请求的 http 头来判断是否命中强缓存,如果命中强缓存,则直接从缓存中获取资源,不会将请求发送到服务器。
若未命中强缓存,则浏览器会继续查看是否命中协商缓存,若命中协商缓存 ,则向服务器端发送请求,由服务器端判断浏览器本地缓存是否失效,若未失效可以继续使用,服务器则不会返回资源信息,返回 304 状态码,允许浏览器从本地缓存获取资源。
若未命中协商缓存,服务器会将完整的资源返回给浏览器,浏览器加载新资源,并更新缓存。