前言
缓存机制随处可见,优秀的缓存机制可以缩短网页请求资源的事件,减少延迟,并且由于缓存文件可以重复利用还可以减少带宽,降低网络负荷,从而提高我们的性能优化。
一、前端缓存概述
1.什么是缓存
- 缓存就是数据交换的缓冲区(又称作Cache),当某一硬件要读取数据时,会首先从缓存中查找需要的数据,找到了则直接执行,找不到的话则从内存中查找。由于缓存的运行速度比内存快得多,故缓存的作用就是帮助硬件更快地运行。
- 其原始意义是指访问速度比一般随机存取存储器(RAM)来得快的一种RAM,一般而言它不像系统主存那样使用DRAM技术,而使用昂贵但较快速的SRAM技术。
- 现在高速缓存的概念已被扩充,不仅在CPU和主内存之间有Cache而且在内存和硬盘之间也有Cache(磁盘高速缓存),乃至在硬盘与网络之间也有某种意义上的Cache - Internet临时文件夹──凡是位于速度相差较大的两种硬件之间的、用于协调两者数据传输速度差异的结构,均可称之为Cache。随着应用软件的增多,垃圾也越来越多。智能缓存的开发与应用,对有意义的缓存保留,无用的缓存智能清理或者进行暂时压缩处理。
2.什么是前端缓存
前端缓存主要是指HTTP缓存和浏览器缓存,前端缓存可以加快页面加载速度、减轻服务器负担、减少延迟与网络阻塞、提高用户体验、支持离线使用等,能够有效的提升网站与应用的性能,但同时也面临着缓存过期、用户安全、缓存清除等问题。
3.前端缓存分类
二、HTTP缓存
1.什么是HTTP缓存
http缓存指的是: 当客户端向服务器请求资源时,会先抵达浏览器缓存,如果浏览器有“要请求资源”的副本,就可以直接从浏览器缓存中提取而不是从原始服务器中提取这个资源。
- 常见的http缓存只能缓存get请求响应的资源,对于其他类型的响应则无能为力,所以后续说的请求缓存都是指GET请求。
- http缓存都是从第二次请求开始的。第一次请求资源时,服务器返回资源,并在respone header头中回传资源的缓存参数; 第二次请求时,浏览器判断这些请求参数,命中强缓存就直接200,否则就把请求参数加到request header头中传给服务器,看是否命中协商缓存,命中则返回304,否则服务器会返回新的资源。
2.HTTP缓存分类
HTTP 缓存分为:强缓存和协商缓存。
3.HTTP 缓存流程图
4.强缓存
浏览器在请求某一资源时,会先获取该资源缓存的header信息,判断是否命中强缓存(cache-control和expires信息),若命中直接从缓存中获取资源信息,包括缓存header信息;本次请求根本就不会与服务器进行通信。状态码为200。可通过设置响应头中的
Cache-Control
和
Expires
字段来控制缓存策略。
强缓存的两种常用设置:
- Cache-Control: 是一个用于控制缓存行为的响应头字段。常见的取值有
public
:表示响应可以被客户端和代理服务器缓存。private
:表示响应只能被客户端缓存,中间的代理服务器不能缓存。no-cache
:表示客户端缓存该资源,但在使用之前必须先与服务器确认是否是最新的。max-age=<seconds>
:指定资源在缓存中的最大有效时间,单位为秒 类型描述public客户端和代理服务器都可以缓存该资源;客户端在xxx秒的有效期内,如果有请求该资源的需求的话就直接读取缓存,status code:200 ,如果用户做了刷新操作,就向服务器发起http请求private只让客户端可以缓存该资源,代理服务器不缓存;客户端在xxx秒内直接读取缓存,status code:200immutable客户端在xxx秒的有效期内,如果有请求该资源的需求的话就直接读取缓存,status code:200 ,即使用户做了刷新操作,也不向服务器发起http请求no-cache跳过设置强缓存,但是不妨碍设置协商缓存;一般如果你做了强缓存,只有在强缓存失效了才走协商缓存的,设置了no-cache就不会走强缓存了,每次请求都回询问服务端no-store不缓存,这个会让客户端、服务器都不缓存,也就没有所谓的强缓存、协商缓存了示例:Cache-Control: max-age:3600, s-maxage=3600, publicCache-Control: no-cacheCache-Control: no-store
- max-age 指令给出了缓存过期的相对时间,单位为秒数。当其与 Expires 同时出现时,max-age 的优先级更高。但往往为了做向下兼容,两者都会经常出现在响应首部中。同时 max-age 还可在请求首部中被使用,告知服务器客户端希望接收一个存在时间(age)不大于多少秒的资源。 public/private 决定资源是否可以在代理服务器进行缓存,其中,public 表示资源在客户端和代理服务器都可以被缓存,private 表示资源只能在客户端被缓存,拒绝资源在代理服务器缓存。默认值为 private。 s-maxage 决定了代理服务器缓存的时长,必须和 public 属性一起使用才能起到作用。 no-cache 表示强制进行协商缓存,如果某一资源的Cache-control中设置了no-cache,那么该资源会直接跳过强缓存的校验,直接去服务器进行协商缓存。 no-store 表示禁止任何缓存策略。
- Expires: 是http1.0的规范,是一个响应头字段,表示资源的过期时间,它的值是一个绝对时间的GMT格式的时间字符串。参考时间为浏览器本地时间(也就是说如果人为把电脑时间往后调整该缓存就会过期),浏览器会将这个时间与客户端当前时间进行比较,判断资源是否过期。这个时间代表这这个资源的失效时间,只要发送请求时间是在Expires之前,那么本地缓存始终有效,则在缓存中读取数据。失效的时间是一个绝对时间,当服务器与客户端时间偏差较大时,会导致缓存混乱。如果同时出现Cache-Control:max-age和Expires,那么max-age优先级更高。
Expires: Wed, 01 Jun 2023 12:00:00 GMT
- Cache-Control字段要比Expires字段更加准确,当两个字段同时存在与某条响应中浏览器会采取Cache-Control的规则,而Expires更多的意义是为了兼容不支持HTTP/1.1的浏览器使用缓存(如果连HTTP/1.0都不支持的话也用不了缓存机制了)。强缓存的意义是为了减少资源的请求数量,但是有些文件我们无法保证在强缓存有效的时间内不修改,这个时候我们就需要使用协商缓存策略。
总结:
在强缓存生效期间,浏览器会直接从本地缓存中获取资源,并且不会发送请求到服务器。只有当缓存过期或不符合缓存规则时,浏览器才会发送请求到服务器进行验证,即执行协商缓存机制。
强缓存可以有效减少网络请求,提升网页加载速度和用户体验。但需要注意的是,如果设置的缓存时间过长,资源的更新可能不会立即生效,导致用户无法获取最新的资源。因此,在设置强缓时,需要综合考虑缓存时间和资源的更新频率。
5.协商缓存
协商缓存就是资源在强缓存失效(或者没有设置强缓存)后,浏览器携带特定缓存标识(HTTP请求头中的某些字段)向服务器发送请求,由服务器根据缓存标识决定是否使用缓存的过程。如果缓存可用则返回状态码304以及在响应头中设置对应字段,否则返回新的资源以及状态码为200。和强缓存一样,可以通过两对HTTP的头部字段实现协商缓存:Last-Modified(响应头字段)<—>If-Modified-Since(请求头字段)和Etag(响应头字段)<——>If-None-Match(请求头字段)。
协商缓存的工作流程如下:
1.浏览器发送请求到服务器,请求特定资源。
2.服务器接收到请求后,检查资源的缓存策略,并在响应头中设置缓存验证相关的字段,如
ETag
和
Last-Modified
。
- 如果响应头中包含了
ETag
,浏览器会将其保存下来。 - Last-Modified(最后修改时间):资源的最后修改时间。
3.浏览器收到响应后,检查响应头中的缓存验证字段。
- 如果响应头中包含了
ETag
,浏览器会将其保存下来。 - 如果响应头中包含了
Last-Modified
,浏览器也会将其保存下来。
4.当浏览器再次请求该资源时,会在请求头中包含缓存验证字段,用于告知服务器上一次请求时使用的标识和最后修改时间。
- If-None-Match:浏览器将上次请求中服务器返回的
ETag
值放在该字段中。 - If-Modified-Since:浏览器将上次请求中服务器返回的
Last-Modified
值放在该字段中。
5.服务器接收到带有缓存验证字段的请求后,进行缓存验证:
- 如果验证结果为资源未发生变化(ETag 匹配或 Last-Modified 时间之后未修改),服务器返回状态码 304 Not Modified,响应头中不包含新的资源内容。
- 如果验证结果为资源已发生变化,服务器返回新的资源内容,并在响应头中包含新的
ETag
和Last-Modified
。
6.浏览器根据服务器的响应进行处理:
- 如果服务器返回 304 Not Modified,表示资源未发生变化,浏览器会使用缓存的资源。
- 如果服务器返回新的资源内容,浏览器会使用新的资源,并更新缓存。
通过协商缓存,可以在资源发生变化时及时更新缓存,减少不必要的网络传输和服务器负载。它与强缓存一起使用,可以提供更灵活和高效的缓存策略,以提高应用的性能和用户体验。
三、浏览器缓存
1.Service Worker Cache
一种在浏览器后台运行的JavaScript脚本,它可以拦截和处理网页发出的网络请求,以及管理缓存和离线数据。Service Worker可以让网页在离线状态下仍能正常访问,并且可以提高网页的性能和响应速度。它由开发者编写的额外的脚本控制,且缓存位置独立。
Service Worker可以实现以下功能:
- 离线缓存:Service Worker可以缓存网页资源,使得用户在离线状态下仍能访问已经缓存的资源。
- 推送通知:Service Worker可以接收来自服务器的推送通知,并在用户离线时显示通知。
- 资源拦截:Service Worker可以拦截网页发出的网络请求,并根据需要返回缓存中的资源或者从服务器请求最新的资源。
- 跨域通信:Service Worker可以和其他域名的网页进行通信,从而实现一些跨域的功能。
如果 Service Worker 没能命中缓存,一般情况会使用 fetch() 方法继续获取资源。这时浏览器就去 memory cache 或者 disk cache 进行下一次找缓存的工作了。经过 Service Worker 的 fetch() 方法获取的资源,即便它并没有命中 Service Worker 缓存,甚至实际走了网络请求,也会标注为 from ServiceWorker。
注意:为了保证安全性,Service Worker只能在HTTPS协议下使用。
2.Memory Cache
memory cache(内存缓存)是存储在浏览器内存中的。其优点为获取速度快、优先级高,从内存中获取资源耗时为 0 ms,而其缺点也显而易见,比如生命周期短,当网页关闭后内存就会释放,同时虽然内存非常高效,但它也受限制于计算机内存的大小,是有限的。那么如果要存储大量的资源,这是还得用到磁盘缓存。
3.Disk Cache
disk cache(硬盘缓存)是存储在计算机硬盘中的。其优缺点与 memory cache 正好相反,比如优点是生命周期长,不触发删除操作则一直存在,而缺点则是获取资源的速度相对内存缓存较慢。 disk cache 会根据保存下来的资源的 HTTP 首部字段来判断它们是否需要重新请求,如果重新请求那便是强缓存的失效流程,否则便是生效流程。 获取顺序:
- 浏览器会率先查找内存缓存,如果资源在内存中存在,那么直接从内存中加载;
- 如果内存中没找到,接下去会去磁盘中查找,找到便从磁盘中获取;
- 如果磁盘中也没有找到,那么就进行网络请求,并将请求后符合条件的资源存入内存和磁盘中。
四、存储型缓存
浏览器存储型缓存包含了 Cookie、Web Storage、IndexedDB、WebSQL 等,它们也是我们日常开发中经常会接触的缓存,其是造成“登录一个网站后再次访问的时候就已经是登录状态”现象的主要原因。
1.Cookie
Cookie 的存储空间很小,不能超过 4KB,因此这一缺点也限制了它用于存储较大容量数据的能力。 不建议将非用户身份类的数据存储在 Cookie 中,因为 Cookie 在同域下会伴随着每一次资源请求的请求报头传递到服务端进行验证,试想一下如果大量非必要的数据存储在 Cookie 中,伴随着请求响应会造成多大的无效资源传输及性能浪费。 在 Cookie 存储 API 方面,浏览器提供的原始 API 使用起来也不是特别方便,比如:
// 存储 Cookie
document.cookie='username=xiaoming; domain=test.fly.com'
// 读取 Cookie
// 只能通过 document.cookie 读取所有 Cookie 并进行字符串截取,非常不便
// 删除 Cookie
let date = new Date()
date.setTime(date.getTime() - 10000) // 设置一个过期时间
document.cookie=`username=xaioming; domain=test.fly.com; expires=${date.toGMTString()}`
如此操作起来会编写大量重复糟心的代码,因此封装 Cookie 的增删改查操作十分必要。推荐使用 js-cookie 库,其 API 操作如下:
import Cookies from 'js-cookie'
// 存储 Cookie
Cookies.set('username', 'xiaoming', { domain: 'test.fly.com' })
// 读取 Cookie
Cookies.get('username')
// 删除 Cookie
Cookies.remove('username')
2.Web Storage
Web Storage 即为 Session Storage 和 Local Storage。
在验证用户身份及维持状态方面,Cookie 有明显的特点和优势,但其并不是存储网页数据的小能手,相反 Web Storage 在这方面却有显著的优势。 Web Storage 作为 HTML5 推出的浏览器存储机制,其又可分为 Session Storage 和 Local Storage,两者相辅相成。 Session Storage 作为临时性的本地存储,其生命周期存在于网页会话期间,即使用 Session Storage 存储的缓存数据在网页关闭后会自动释放,并不是持久性的。而 Local Storage 则存储于浏览器本地,除非手动删除或过期,否则其一直存在,属于持久性缓存。 Web Storage 与 Cookie 相比存储大小得到了明显的提升,一般为 2.5-10M 之间(各家浏览器不同),这容量对于用于网页数据存储来说已经十分充足。 我们再来看一下 Web Storage 相关的操作 API(以 Local Storage 为例):
// 存储
localStorage.setItem('username', 'xiaoming')
// 读取
localStorage.getItem('username')
// 删除
localStorage.removeItem('username')
在存储简单的数据类型时,Web Storage 提供的原始 API 可以轻松完成任务,但是一旦数据类型变为 Object 类型时,其应付起来就变得捉襟见肘,主要原因在于使用 Web Storage 存储的数据最终都会转化成字符串类型,比如:
localStorage.setItem('length', 15)
localStorage.getItem('length') // 最终获取的会是字符串 '15'
而存储对象时如果没有提前采用序列化方法 JSON.stringify 转化为字符串对象,那么最终获取的值会变成 **[object Object]**。 因此 Web Storage 的原始存储方案会存在繁碎的序列化与反序列化的缺点:
let userinfo = { name: 'xiaoming', age: 18 }
// 存储时进行序列化操作
localStorage.setItem('userinfo', JSON.stringify(userinfo))
// 获取时进行反序列化操作
JSON.parse(localStorage.getItem('userinfo'))
我们可以对其API进行二次封装,也可以使用目前 npm 市场上也有相关封装 Web Storage 的包,比如 web-storage-cache。
3.IndexedDB和WebSQL(了解)
- IndexedDB 和 WebSQL 都是一种低级API,用于客户端存储大量结构化数据。
- 该API使用索引来实现对该数据的高性能搜索。
- 不同的是IndexedDB是非关系型,而WebSQL是关系型。
- WebSQL官方不在维护,但兼容性较好
- IndexedDB在维护,兼容性较差
五、优先级
- Service Worker: 缓存由于其可以完全控制网络请求,因此具有最高的优先级,即使是强制缓存也可以被它所覆盖。
- 强制缓存: 如果存在强制缓存,并且缓存没有过期,则直接使用强制缓存,不需要向服务器发送请求。
- 协商缓存:如果强制缓存未命中,但协商缓存可用,则会向服务器发送条件请求,询问资源是否更新。如果服务器返回 304 Not Modified 响应,则直接使用缓存。
- Web Storage 缓存:Web Storage 缓存的优先级最低,只有在网络不可用或者其他缓存都未命中时才会生效。
六、前端缓存的性能优化策略
Web缓存性能优化是一种提高网站加载速度和提高用户体验的方法。通过使用缓存,可以减少服务器的负载和网络延迟,从而提高页面的响应速度。以下是一些关于Web缓存性能优化的建议:
- 频繁变动的资源,比如HTML,采用协商缓存
- CSS、JS、图片等资源采用强缓存,避开启发式缓存,使用 contenthash 命名
- 缓存API响应,在相同请求再次发生时,可以直接从缓存中获取结果,而无需重新查询API
- 采用效率更高的br压缩算法,可以减小传输文件的大小,从而缩短加载时间
- 合并请求,如果把多个访问小文件的请求合并成一个大的请求,虽然传输的总资源还是一样,但是减少请求,也就意味着减少了重复发送的 HTTP 头部
- 延迟加载,对于不需要立即显示的资源(如图片或视频),可以使用延迟加载技术。这样,只有当用户滚动到这些资源时,它们才会开始加载
- 善用preload和prefetch,我们可以优化浏览器资源加载的顺序和时机
- 善用base64,有效命中memory cache
- 使用HTTP/2,HTTP/2协议提供了性能改进,如多路复用和服务器推送。启用HTTP/2可以进一步提高网站性能
版权归原作者 xz鹏 所有, 如有侵权,请联系我们删除。