关于前端性能优化,一直是一个老生常谈的话题,在性能优化的诸多方法中,缓存可以说是性能优化中简单高效的一种优化方式了。
那么浏览器缓存可以在哪方面给我们带来实质性的性能优化呢?
我们知道从我们从输入 URL 到页面加载完成,大致可以分为5个步骤
浏览器缓存可以帮助我们在第3和第5步骤中优化性能。利用浏览器存储机制,将一部分数据保存在客户端,从而减少对服务器的请求降低服务器的压力,提升效率。
一个优秀的缓存策略可以缩短网页请求资源的距离,减少延迟,并且由于缓存文件可以重复利用,还可以减少带宽,降低网络负荷。
对于我这种菜鸡而言,对于浏览器缓存机制的理解就是HTTP 缓存,但查阅资料发现浏览器缓存可以分为4种,而且它们按照获取资源时请求的优先级依次排列如下:
我们依次来看一下吧,乌拉~
Memory Cache 也就是内存中的缓存,主要包含的是当前中页面中已经抓取到的资源,例如页面上已经下载的样式、脚本、图片等,从效率上来说,它是响应速度最快的一种缓存。
那我们是否可以无限制的使用Memory Cache呢?答案是否定的,因为我们电脑的内存是很有限的。一般情况下Base64 格式的图片或者小一点的js文件会放在Memory Cache中,大一点的文件我们一般放在磁盘中,比如这张图:
图中这些文件都是使用缓存中的数据
Service Worker 是运行在浏览器背后的独立线程,一般可以用来实现缓存功能。这是网上对它的介绍,后发现使用 Service Worker的话,传输协议必须为 HTTPS。因为 Service Worker 中涉及到请求拦截,所以必须使用 HTTPS 协议来保障安全。它可以帮我们实现离线缓存、消息推送和网络代理等功能。
Service Worker 的生命周期包括 install、active、working 三个阶段。一旦 Service Worker 被 install,它将始终存在,只会在 active 与 working 之间切换,除非我们主动终止它。这是它可以用来实现离线存储的重要先决条件。
发现一个很不错的Service Worker实践文章,有兴趣的小伙伴可以看一下借助Service Worker和cacheStorage缓存及离线开发
HTTP 缓存是我们日常开发中最为熟悉的一种缓存机制,也是最主要、最具有代表性的缓存策略。HTTP缓存有多种规则,根据是否需要重新向服务器发起请求来分类,它可以分为强缓存和协商缓存。
强制缓存如果生效,不需要再和服务器发生交互,而对比缓存不管是否生效,都需要与服务端发生交互。
两类缓存规则可以同时存在,强制缓存优先级高于对比缓存,也就是说,当执行强制缓存的规则时,如果缓存生效,直接使用缓存,不再执行对比缓存规则。
可以理解为无须验证的缓存策略,强制缓存。对强缓存来说,响应头中有两个字段 Expires/Cache-Control 来表明规则。
Expires
Expires用来设置过期时间,超过这个时间点就表示资源过期。但是会有一个问题,由于这个时间使用时间戳表示,客户端和服务端可能会存在差别,这可能造成缓存生命周期出错。在HTTP/1.1中加入了Cache-Control,并且两个同时存在时也是 Cache-Control 的优先级更高。
Cache-Control
Cache-Control 有几个值:
1. private: 客户端可以缓存,表明响应只能被单个用户(可能是操作系统用户、浏览器用户)缓存,是非共享的,不能被代理服务器缓存。
2. public: 客户端和代理服务器都可缓存
3. max-age=xxx: 缓存的内容将在 xxx 秒后失效
4. no-cache: 需要使用对比缓存来验证缓存数据
5. no-store: 禁止缓存,每次请求都要向服务器重新获取数据
6. s-maxage 但仅适用于共享缓存
浏览器与服务器合作之下的缓存策略
缓存的资源到期了,并不意味着资源内容发生了改变,如果和服务器上的资源没有差异,实际上没有必要再次请求。客户端和服务器端通过某种验证机制验证当前请求资源是否可以使用缓存。
如果服务端提示缓存资源未改动(Not Modified),资源会被重定向到浏览器缓存,这种情况下网络请求对应的状态码是 304。反之返回 200 就相当于重新请求了一遍资源并替换旧资源。
我们具体来看一下协商缓存时的请求头Last-modified/If-Modified-Since 和 Etag/If-None-Match;
Last-Modified 是一个时间戳,它表示服务器端资源的最后修改时间。
如果我们启用了协商缓存,它会在首次请求时随着 Response Headers 返回。第一次请求之后,浏览器记录这个时间,再次请求时,请求头部带上 If-Modified-Since 即为之前记录下的时间。
服务器端收到带 If-Modified-Since 的请求后会去和资源的最后修改时间对比。若修改过就返回最新资源,状态码 200,若没有修改过则返回 304;
但其实使用Last-modified会有弊端存在,由于这个时间是浏览器自己记录的。
由于Last-modified 只能精确到秒,如果我们修改文件的速度过快(100ms)那么,浏览器旧纪录不到这个时间,导致文件没有实时更新。
或者我们编辑了文件,但是没有修改内容再次提交时更新了Last-Modified ,这就导致服务器会有一次完整的响应请求
那么怎么解决这个问题呢?Etag 作为 Last-Modified 的补充出现了
Etag 是由服务器为每个资源生成的唯一的hash字符串,这个标识字符串是基于文件内容编码的,只要文件内容不同,它们对应的 Etag 就是不同的,反之亦然。因此 Etag 能够精准地感知文件的变化。
注意:实际使用 ETag/Last-modified 要注意保持一致性,做负载均衡和反向代理的话可能会出现不一致的情况。计算 ETag 也是需要占用资源的,如果修改不是过于频繁,看自己的需求用 Cache-Control 是否可以满足。
“推送缓存”是针对HTTP/2标准下的推送资源设定的。推送缓存是session级别的,当 session 终止时,缓存也随之释放。在以上三种缓存都没有的情况下,它才会被使用。Push Cache是HTTP2新出的特性,他也将是未来未来的趋势,push cache的特性这里简单介绍:
https://www.cnblogs.com/vajoy/p/5341664.html
https://www.cnblogs.com/chenqf/p/6386163.html