说起缓存,每个前端开发者都不会陌生。它是很常见的前端性能优化手段之一,无论在节省带宽、提高加载和渲染速度、减少网络阻塞,以及提高用户体验上,都发挥着很重要的作用。
缓存过程页面的加载,可能会经历如上图所示的缓存过程。之所以会说可能,是因为有些缓存在一次请求中,不会经历。比如:如果请求命中了强缓存,那浏览器就直接返回结果了,不会进入协商缓存流程。下面我们会详细讲述下这些缓存的作用以及区别。
DNS缓存通常,我们在访问一个网站时,会先在浏览器地址栏中输入一串URL,这个URL的格式大概是这个样子的:
协议://域名或IP地址:端口/路径?参数
。从URL中,我们可以看到
域名
和
IP地址
都可以帮我们定位到指定的服务主机上。
IP地址
就是每一台物理机的唯一逻辑地址,可以很快找到对应主机,但是
域名
就不同了,它和
IP地址
是映射关系,它可以对应一个或多个
IP地址
。那我们想要通过
域名
查找到对应的服务器,就需要先找到它映射的
IP地址
是哪个,再跟进这个
IP地址
找到服务器。
域名
查找
IP地址
的过程会对网络请求带来一定的损耗,所以浏览器在第一次获取到
IP地址
后,会将其缓存起来。下次相同域名再次发起请求时,浏览器会先查找本地缓存,如果缓存有效,则会直接返回该
IP地址
,否则会继续开始寻址之旅。
memory cache本着操作系统先读内存,再读硬盘的常理,浏览器会先从
memory cache
中读取缓存,再去
disk cache
中读取。由于
memory cache
是短期存储,在浏览器tab关闭后,缓存便会失效。当数据量过大,即使tab不关闭,缓存依然会失效。
那哪些资源会存入
memory cache
中呢?答:几乎所有资源。
看上面这张图的资源加载,全部通过
preload
的方式加载资源,在第一次加载时,全部存储到了
disk cache
中,如下图:
而在我刷新页面后(f5刷新),
script
资源会存储到了
memory cache
中,而
stylesheet
依然存储在
disk cache
中。
另外,
prefetch
加载的资源,通常也会放入
disk cache
。
disk cache硬盘缓存又叫HTTP缓存,它也是浏览器缓存中最重要的内容。
disk cache是永久缓存,根据HTTP协议头的缓存相关字段来判断缓存是否有效。它又分为两种:
强缓存
和
协商缓存
。HTTP缓存的详细过程如下图:
浏览器发起请求时,首先检测有没有缓存,若没有缓存,直接向服务端发起请求,若有,则会遵循HTTP缓存规则,在命中强缓存或协商缓存时,会将缓存结果返回,不然则从服务端获取数据。
强缓存对于强缓存,控制它的字段分别是:
Expires
和
Cache-Control
,其中
Cache-Control
优先级比
Expires
高。
- Expires
Expires
是
HTTP1.0
控制网页缓存的字段,其值为服务器返回该请求结果缓存的到期时间,形如:
Mon, 23 Nov 2020 08:39:58 GMT
,即再次发起该请求时,如果客户端的时间小于
Expires
的值时,直接使用缓存结果。
Expires
控制缓存的原理是使用客户端的时间与服务端返回的时间做对比,那么如果客户端与服务端的时间因为某些原因(例如时区不同;客户端和服务端有一方的时间不准确)发生误差,那么强制缓存则会直接失效,这样的话强制缓存的存在则毫无意义。
- Cache-Control
基于上述
Expires
的缺陷,在
HTTP1.1
中,用
Cache-Control
替代了
Expires
。当然,为了兼容
HTTP1.0
和
HTTP1.1
,在实际应用中,这两个值都会设置。
Cache-Control
的指令分为:
缓存请求指令
和
缓存响应指令
,值分别如下:
- 缓存请求指令
Cache-Control | 含义 |
---|
- 缓存响应指令
Cache-Control | 含义 |
---|
从前面介绍的内容,我们知道,浏览器在发起请求时,会先判断是否命中强缓存,如果没有命中,则会去验证协商缓存是否被命中。协商缓存有两对字段,他们的验证方法分别如下:
- Last-Modified & If-Modified-Since
- 服务器通过
字段,将资源最后一次修改的时间告知客户端Last-Modified
- 浏览器将
和资源内容一起缓存起来Last-Modified
- 在浏览器下一次请求相同资源时,会从自己的缓存中找出“不确定是否过期的”缓存。因此在请求头中将上次的
的值写入到请求头的Last-Modified
字段。If-Modified-Since
- 此时,服务器会将
的值与If-Modified-Since
字段进行对比。如果相等,则表示未修改,响应304,不会返回任何内容,告知客户端可以使用缓存;反之,则表示修改了,响应200,并返回数据。Last-Modified
- 浏览器会将新的内容和
缓存起来。Last-Modified
- ETag & If-None-Match
如果资源在1s内有多次修改的话,
Last-Modified
是监听不到的,因为它的缓存时间是以s为过期时间单位的。对于这种情况,
HTTP1.1
引入了以hash形式记录资源内容是否修改的
ETag
来更准确的判断是否命中缓存。
ETag
的验证方法与
Last-Modified
是一致的,只是
ETag
是以hash生成的特殊标识。服务端会将浏览器请求头中的
If-None-Match
与
ETag
进行比较,若相等,则命中缓存,返回304;否则返回200和最新的资源内容。
ETag
的优先级是高于
Last-Modified
的,为了兼容不同版本的协议,通常项目中,都会把两者都设置。
CDN缓存CDN作为一种服务端缓存方案,在我们平时的工作中,并不陌生。CDN旨在解决的最重要的问题就是网络延迟,CDN服务商会将源站的资源缓存到遍布全国的高性能加速节点上,当用户访问相应的业务资源时,用户会被调度至最接近的节点最近的节点IP返回给用户,在web性能优化中,它主要起到了,缓解源站压力,优化不同用户的访问速度与体验的作用。
前面我们了解到,每次的资源请求发起时,浏览器都会检测本地是否有缓存并且未过期,如果是,则直接使用缓存,否则则会向服务端发起请求。如果此时在浏览器和服务器之间增加一层CDN服务,整个访问过程会是什么样的呢?
如上图所示,客户端浏览器先检查是否有本地缓存是否过期,如果过期,则向CDN边缘节点发起请求,CDN边缘节点会检测用户请求数据的缓存是否过期,如果没有过期,则直接响应用户请求,此时一个完成http请求结束;如果数据已经过期,那么CDN还需要向源站发出回源请求(back to the source request),来拉取最新的数据。
可以看出,CDN的优点很明显了:
- CDN节点解决了跨运营商和跨地域访问的问题,访问延时大大降低;
- 大部分请求在CDN边缘节点完成,CDN起到了分流作用,减轻了源站的负载。
以上讲述了普通业务中,一次请求经历的直接相关缓存内容。还有其他的缓存,比如
Service Worker
,Nginx缓存等。有兴趣的同学可以自己去了解下,有很多挺不错的分享文章。
参考文章[1].一文读懂前端缓存 - https://juejin.im/post/6844903747357769742?utm_source=gold_browser_extension
[2].Cache Control MDN - https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Cache-Control
[3].A Tale of Four Cache - https://calendar.perfplanet.com/2016/a-tale-of-four-caches/
[4].浏览器缓存机制剖析- http://louiszhai.github.io/2017/04/07/http-cache/
[5].一文读懂前端缓存(超详细) - https://www.jianshu.com/p/227cee9c8d15?from=singlemessage