前端需要了解的浏览器缓存知识

前言

作为一个前端开发者,每天都在和浏览器打交道,对于浏览器缓存,相信都不会陌生,同时它也是我们日常开发中存在的一个非常重要的优化手段,无论在节省带宽、提高加载和渲染速度、减少网络阻塞,以及提高用户体验上,都有重要的作用。

为什么需要浏览器缓存?

对于浏览器缓存,主要针对的是前端的静态资源,最好的效果是,在发起请求后,拉取相应的静态资源,并保存在本地。如果服务器的静态资源没有更新,那么在下次请求的时候,直接从本地读取资源即可;如果服务器的静态资源已经更新,那么我们再次请求的时候,就要去服务器拉取新的资源,并保存到本地。这样就大大减少了请求次数,提高网站性能。这就用到浏览器的缓存策略了。

总之使用浏览器缓存,有以下优点:

  • 加快客户端网页加载速度,减少用户等待时间,提升用户体验,直接从内存或磁盘中取缓存数据肯定是比从服务器请求更快的。
  • 减少了冗余的数据传输。
  • 减少服务器的负担,大大提升了网站性能。

DNS缓存

这是我们输入网址后,最开始的一个缓存;通常我们输入一个网址,它包含了域名端口可以指定唯一的IP地址,然后建立连接进行通信,而域名查找IP地址的过程就是DNS解析

www.baidu.com (域名) - DNS解析 -> 180.76.76.76 (IP地址)

这个过程会对网络请求带来一定的损耗,所以浏览器在第一次获取到IP地址后,会将其缓存起来。下次相同域名再次发起请求时,浏览器会先查找本地缓存,如果缓存有效,则会直接返回该IP地址,否则会继续开始寻址之旅。
关于寻址过程请看我之前的文章 浏览器从输入URL到页面渲染加载的过程(浏览器知识体系整理)

缓存读写顺序

前端需要了解的浏览器缓存知识

备注:

memory cache =》浏览器本地缓存

disk cache =》 硬盘缓存

  1. 先在 浏览器缓存 中查找,如果有,直接加载。

  2. 如果 浏览器缓存 中不存在,则在 硬盘 中查找,这里又细分:

  • 如果有强缓存且未失效,则使用强缓存,不请求服务器。

  • 如果有强缓存但已失效,使用协商缓存,比较后确定 304 还是 200;

  1. 如果硬盘中也不存在,向服务器发起网络请求。

  2. 请求获取的资源缓存到硬盘和内存。

下面将从 缓存位置缓存策略 两个角度介绍浏览器缓存。

缓存位置

memory cache(浏览器本地缓存)

是浏览器内存中的缓存,相比于 disk cache 它的特点是读取速度快,但容量小,且时效性短;不受开发者控制,也不受HTTP协议头的约束。一旦浏览器 tab 页关闭,memory cache 就将被清空,再次重新打开相同页面时不再出现from memory cache的情况。

disk cache(硬盘缓存)重点!!!

硬盘缓存取决于HTTP中的响应头信息,它也是浏览器缓存中最重要的内容。因为DNS缓存它主要是做一个ip地址查找并且是自主完成的,memory cache 也是不受控制,算是一个黑盒。所以剩下的可以受我们控制的硬盘缓存的重要性就不言而喻了,大多优化手段也是针对硬盘缓存

根据 HTTP 响应头的各类字段进行判定资源的缓存规则,比如是否可以缓存,什么时候过期,过期之后是否需要重新发起请求呢?相比于 memory cache 的 disk cache 拥有存储空间时间长等优点。

HTTP所控制下的 disk cache 缓存分为强缓存协商缓存

缓存策略 – 强缓存和协商缓存

前端需要了解的浏览器缓存知识

采用网络图片进行学习。

根据 HTTP header 的字段将缓存分为两个部分,分别是强缓存协商缓存

  1. 强缓存:使用强缓存策略时,如果缓存资源在过期时间内,是的话直接从本地缓存中读取资源,不与服务器进行通信。
  2. 协商缓存:如果强缓存失效后,客户端将向服务器发出请求,进行协商缓存。浏览器携带上一次请求返回的响应头中的 缓存标识 向服务器发起请求(如ETag、Last-Modified等),由服务器判断资源是否更新。如果资源没有更新,则返回状态码 304 Not Modified,告诉浏览器可以使用本地缓存;否则返回新的资源内容。强缓存优先级高于协商缓存,但是协商缓存可以更加灵活地控制缓存的有效性。

强缓存的字段有:ExpiresCache-Control。协商缓存的字段有:Last-ModifiedETag

  • ExpiresCache-Control 都被设置的时候,浏览器会优先考虑后者。
  • Last-ModifiedETag 都被设置的时候,浏览器会优先考虑后者。

1)强缓存

当客户端发出一个请求到服务器,服务器希望你把资源缓存起来,于是在响应头中加入了这些内容:

Cache-Control: max-age=3600  // 我希望你把这个资源缓存起来,缓存时间是3600秒(1小时)
Expires: Mon Oct 17 2023 16:10:32 GMT  // 到达指定时间过期
Date: Mon Oct 16 2023 13:30:30 GMT 
Etag:W/"121-171ca289ebf",// (后面协商缓存内容)这个资源的编号是W/"121-171ca289ebf"
Last-Modified: Mon Oct 16 2023 09:20:10 GMT ,// (后面协商缓存内容)这个资源的上一次修改时间

Cache-ControlExpires分别是HTTP/1.1 和 HTTP/1.0的内容,为了兼容 HTTP/1.0 和 HTTP/1.1,实际项目中两个字段我们都会设置。

浏览器收到这个响应之后就会做下面的事情

  • 浏览器把这次请求得到的响应体缓存到本地文件中
  • 浏览器标记这次请求的请求方法和请求路径
  • 浏览器标记这次缓存的时间是3600秒
  • 浏览器记录服务器的响应时间是格林威治时间2023-10-16 09:20:10

这一次的记录非常重要,它为以后浏览器要不要去请求服务器提供了依据。

判断缓存是否有效就是通过把 max-age + Date,得到一个过期时间,看看这个过期时间是否大于当前时间,如果是,则表示缓存还没有过期,仍然有效,如果不是,则表示缓存失效。

Expires

ExpiresHTTP/1.0的字段,表示缓存过期时间。Expires 需要在服务端配置(具体配置也根据服务器而定),浏览器会根据该过期日期与客户端时间对比,如果过期时间还没到,则会去缓存中读取该资源,如果已经到期了,则浏览器判断为该资源已经过期需要重新从服务端获取。由于 Expires 是一个绝对时间,所以会局限于客户端时间的准确性,从而可能会出现浏览器判断缓存失效的问题。所以出现了Cache-Control,如下是一个 Expires 示例,是一个日期/时间:

Expires: Mon Oct 17 2023 16:10:32 GMT 

到了HTTP/1.0版本,已更改为通过Cache-Controlmax-age来记录了。

Cache-Control

Cache-Controlhttp1.1 时出现的响应头信息,主要通过Cache-Controlmax-age来记录。下面是几个比较常用的设置值:

  • max-age: 最大缓存时间,它是一个相对时间,值的单位是秒,在该时间内,浏览器不需要向浏览器请求。这个设置解决了 Expires 中由于客户端系统时间不准确而导致缓存失效的问题。
  • no-cache: 跳过强缓存,直接进入协商缓存阶段。
  • no-store: 禁止使用缓存,每次都要重新请求数据,不会被缓存到内存和硬盘。
  • public: 响应可以被任何对象(客户端、代理服务器等)缓存。
  • private:响应只能被客户端缓存。

Cache-Control 的值是可以混合使用的,比如:

Cache-Control: private, max-age=0, no-cache

当强缓存失效的时候,则会进入到协商缓存阶段。

2)协商缓存

一旦发现强缓存无效,浏览器会发送一个请求到服务器,服务器根据请求header中的部分信息来判断资源是否更新。如果没有更新,返回304重定向,告诉浏览器资源未更新,可继续使用本地的缓存;否则返回 状态码200 和 新的资源内容,浏览器缓存新的内容。

这里的请求头header,就是加入了

If-Modified-Since: Mon Oct 16 2023 09:20:10 GMT  你好,你曾经告诉我,这个资源的上一次修改时间是格林威治时间2023-10-16 09:20:10,请问这个资源在这个时间之后有发生变动吗?
If-None-Match: W/"121-171ca289ebf"  你好,你曾经告诉我,这个资源的编号是W/"121-171ca289ebf,请问这个资源的编号发生变动了吗?

其实响应头和请求头的对应关系就是 Last-Modify/If-Modify-SinceETag/If-None-Match

之所以要发两个信息,是为了兼容不同的服务器,因为有些服务器只认If-Modified-Since,有些服务器只认If-None-Match,有些服务器两个都认,但是一般来说 If-None-Match 的优先级高于 If-Modified-Since

Last-Modify / If-Modify-Since

浏览器第一次请求一个资源的时候,服务器返回的 header 中会加上 Last-ModifyLast-Modify是一个时间标识该资源的最后修改时间。
当浏览器再次请求该资源时,请求头中会包含 If-Modify-Since,该值为缓存之前返回的 Last-Modify。服务器收到 If-Modify-Since 后,根据资源的最后修改时间判断资源是否更新。

  • 资源未更新,返回304重定向,表示资源未更新可以继续使用缓存中的资源。
  • 资源更新,返回200状态码,返回新的资源,并进行硬盘和浏览器缓存。

缺点:如果资源更新的速度是小于 1 秒的,那么该字段将失效,因为 Last-Modified 时间是精确到秒的。所以有了 ETag

ETag / If-None-Match

与 Last-Modify/If-Modify-Since 不同的是,Etag/If-None-Match 返回的是一个校验码。ETag 可以保证每一个资源是唯一的,资源变化都会导致 ETag 变化。服务器根据浏览器上发送的 If-None-Match 值来判断是否缓存。
与 Last-Modified 不一样的是,当服务器返回 304 Not Modified 的响应时,由于 ETag 重新生成过,response header 中还会把这个 ETag 返回,即使这个 ETag 跟之前的没有变化。

不需要缓存的时候

并不是所有请求都能被缓存,无法被浏览器缓存的请求如下:

  • HTTP 信息头中包含 Cache-Control: no-cachepragma: no-cache(HTTP1.0),或 Cache-Control: max-age=0 等告诉浏览器不用缓存的请求;
  • 需要根据 Cookie、认证信息等决定输入内容的动态请求是不能被缓存的;
  • 经过 HTTPS 安全加密的请求;
  • POST 请求无法被缓存;
  • HTTP 响应头中不包含 Last-Modified/Etag,也不包含 Cache-Control/Expires 的请求无法被缓存;

本文参考

一文读懂浏览器缓存

实践这一次,彻底搞懂浏览器缓存机制

浏览器缓存缓存策略(看完就懂)

原文链接:https://juejin.cn/post/7314567553950253066 作者:许开心

(0)
上一篇 2023年12月21日 上午10:32
下一篇 2023年12月21日 上午10:42

相关推荐

发表回复

登录后才能评论