谈谈性能优化
分析原因, 通过chrome的性能分析工具 Lighthourse、浏览器的performance的性能工具
对于性能优化,从整体项目来看,结合用户体验网页性能和代码层面优化、打包构建优化等,其实一般首先通过监控埋点来进行分析针对性优化
-
通过浏览器的性能监控指标来分析 :TTFB、FP、FCP、LCP、FID、CLS、TBT
- 通过这些指标分析 页面加载速度、交互性、总阻塞时间、接口响应时间等
TTFB、FP (首次绘制)、FCP(首次内容绘制)、LCP(最大内容绘制)、FID(首次输入延迟)、CLS(累计布局偏移)、TBT(总阻塞时间)
-
【window.onerror、window.addEventListener('error', ...)】,通过监控 JavaScript 错误、网络请求失败等异常情况
性能指标分析
- 页面测速工具:https://pagespeed.web.dev/
- Google 提供的网页性能分析工具
- PageSpeed 展示真实用户数据情况,单独服务器上运行,对比lighthouse 结果通常会更准确、更一致
- 深度性能分析测试:https://www.webpagetest.org/
- 性能抓包 、诊断工具,可以看到非常细的加载过程
- 用来分析一个网页在不同地区、设备、浏览器、网络条件下的真实加载表现。
- lighthouse
- Performance
首屏加载性能优化
- 减少http请求
- 按需加载/异步加载
- 组件、路由按需加载
- 体积优化
- 字体文件加载优化
- requestAnimationFrame分步渲染
- defer和async应用
- defer延迟加载 : 加载完后等dom解析完之后再执行。
- async异步加载脚本, 浏览器空闲再执行
- preFetch、preLoad应用
- gzip压缩
- 静态资料配合OSS+CDN加速
- 网络请求速度优化
- 图片懒加载
减少http请求
- 1、利用浏览器缓存
- 强缓存、协商缓存
- 2、合并JS和CSS文件
- 通过webpack/vite构建工具
- 3、图片可以滚动加载、雪碧图
开启gzip
开启gzip之后,效果是非常明显的,压缩后一般能减少响应文件的50%-70%的大小
- 压缩 Gzip,服务端要花时间;解压 Gzip,浏览器要花时间
- 服务器上可以开启gzip, 在客户端中通过构建工具(webpack/vite)也可以进行gzip压缩
- 通过客户端生产gzip的话,可以分担服务端的压力
- 大量的文件需要服务端压缩的话,也是非常消耗服务端的性能
- 如果客户端生成,服务端先判断是否存在的后缀名为gz的文件,直接去拿,不存在就来压缩
- 虽然在客户端开启gzip,打包生产的时候,会慢一些,但是我们发布打包生产只需要打一次包,而服务器是面对多个用户访问的 (每个用户访问的话,对服务器压力是很大的)
- 通过客户端生产gzip的话,可以分担服务端的压力
注意:如果代码体积不是很大的话,可以不开启gzip,因为开启gzip,浏览器解压也需要时间
gzip原理
当请求静态资源时,服务端发现请求资源为gzip的格式时,设置响应头 content-encoding: gzip
- gzip实际上把js文件进行了类似zip压缩包的
- 前端上传js到CDN静态资源服务器上,上传的大小比如是1M, 当浏览器访问的时候,读取的实际上是被压缩过的类似zip压缩的文件,就叫gzip, 如300k
打包优化
- 组件库打包优化
- antd css打包优化
-
在antd4版本中,antd的js会做分包,但是css没有,需要引入
import 'antd/dist/antd.css'- 这样会导致css文件打包出来很大
- 可以利用antd提供的babel插件去优化
-
antd5中,有升级方案,从less => css in js
- 核心就是在js中写css,然后把样式通过style标签插入页面中
-
- antd css打包优化
react性能优化
渲染优化
-
减少render次数
监控render次数的原则: 复杂的组件、顶层的组件才需要
- 简单的组件不需要监控render,没什么必要,简单的组件性能可以忽略不计
- 因为监控组件render次数也是需要成本,如果页面组件太多,需要花时间成本去排查
- 把时间花在刀刃上,主要监控复杂组件和的顶层组件就可以了
- 复杂组件:表格、表单、图表、复杂的配置项等
- 注意顶层组件的render次数
- 如果顶层组件都render多次,那么它下面的子组件也会多次render
- 注意state状态改变,就会触发组件重新渲染,要留意state状态的值触发改变的逻辑
- 对于class类组件,使用
shouldComponentUpdate/PureComponent- 根据 shouldComponentUpdate 的返回值,决定是否对组件进行重新渲染
- PureComponent,会对props 和 state 进行浅比较, 对于基本数据类型会判断值是否相等,而对于引用类型数据判断引用是否相等,不相等则重新渲染
- 函数式组件,使用React.memo
- React.memo只会浅比较props的状态,发生变化则会重新渲染
- 可以缓存函数组件的渲染结果
- 注意PureComponent和React.memo浅比较的问题,当数据是嵌套多层的对象或数组时,当修改了本身的对象其中的属性,但是引用类型地址还是一样的,从而导致没有触发重新渲染
- 应该使用不可变的数据结构,不要直接去修改数据的状态,应该返回一个新的数据状态
- 社区上比较出名有immer这个库,专门解决这个问题,可以确保每次状态更新都会生成一个新的对象,这样浅比较就可以检测到变化,触发组件渲染
- immer的原理,是基于proxy来实现的,会创建一个状态state的副本,当状态被修改的时候,会被proxy拦截监听到会记录下来 ,然后会返回一个新的对象
- immer的性能是比较好的,只复制发生变更部分,而未修改的部分是不会变化的, 未修改的部分还是保持同一个引用,这样减少了内存的消耗,提高这个性能,只有发生修改的部分才会创建新的副本
- 使用
why-did-you-render库来检测组件是否重复渲染
-
useMemo
- 对于复杂的数据,可以进行数据缓存、优化渲染
-
useCallback
- 对于复杂的逻辑,可以进行缓存函数
-
keep-alive
- 组件切换过程中保留状态在内存中,可以防止重复渲染DOM
-
useTransition开启并发渲染,降低任务优先级
- useTransition是降低渲染优先级,可以标记代码渲染优先级不高,可以优先处理其它优先级更高的渲染任务
- useTransition 本质上是为了解决了渲染并发的、不阻塞ui更新的问题
- 其实就是通过Fiber架构,将一个大的渲染任务分为很多小任务,按照优先级来执行这些任务分片,优先级高的会先执行
当一次render要渲染数据量大的时候,可以使用useTransition来进行优化,让页面可以响应用户的交互,但是此方法仅仅优化于reconciler调度的render阶段,没法优化commit提交阶段
加载优化
-
利用React.lazy 和 React.Suspense组件按需加载
-
利用
React.lazy和React.Suspense延迟加载不是立即需要的组件。 -
当前不需要的组件不应该加载,我们应该尽量做到按需加载
-
vue性能优化
vue内部已经做了很多性能优化,比如vue通过数据响应式追踪可以做到精确的组件更新, 以及template模板优化 【静态分析、静态提升】
-
keep-alive
-
v-if与v-show
-
computed
-
v-memo
-
在vue2中合理使用Object.freeze
- 对于不需要响应式的数据,合理使用Object.freeze,可提高性能
- 比如有巨大的数组或Object,并且确信数据不会修改,使用Object.freeze()可以让性能大幅提高
-
在vue3中合理使用toRow/markRaw
-
toRow场景
- 比如临时使用的变量,不需要进行响应式的监听,修改这个对象,不需要页面更新,避免这个性能开销,可以使用
toRow来将响应式对象转换为普通对象
- 比如临时使用的变量,不需要进行响应式的监听,修改这个对象,不需要页面更新,避免这个性能开销,可以使用
-
markRaw场景
- 有些值不应被设置为响应式的,如第三方库,组件等
- 当渲染具有不可变数据源的大列表时,可以不使用响应性代理,从而提高性能
toRow将响应式数据转换为普通数据
markRaw标记一个对象,永远都不成为这个响应式对象
-
小程序性能优化
- 小程序首屏加载优化
- 小程序包体积优化
- setData优化
- setData不能频繁调用, 每次setData都要经过逻辑层和渲染层,逻辑层和渲染层是不同进程。频繁调用每次经过2个进程会影响性能和用户体验
- 图片优化
- 内存优化
- 使用worker
- 主包优化
大数据渲染优化
- 时间切片
- 长任务分割为一个个执行时间很短的任务
- 利用requestAnimationFrame
- 可以使用requestAnimationFrame来分步渲染
- requestAnimationFrame是浏览器在下一次重绘前执行
- 使用webworker线程
- 将大数据的计算放在worker中,不影响主线程
- 虚拟滚动列表
css优化
-
减少回流与重绘
- transform 性能高,可以来替代position来布局元素位置
-
使用transform 不会引起回流与重绘的
-
减少无用的css
-
UI 库全量引入问题优化
-
优先砍掉首屏没用到的样式
-
css缓存复用
-
压缩css文件
CDN+OSS
- OSS 是做存储的,CDN 是做内容加速的
- 一般二者是配合使用的,对象存储oss里面存的就是一些图片、视频、文件等等,都是静态数据,可以给用CDN做加速
- 通过
cdn对内容进行分发,分发对每个地区中,提高用户访问资源的响应速度 - 如果数据已经过期,CDN可以发出回源请求,来拉取最新的数据
- CDN 设置了缓存过期时间,在数据过期后会自动触发回源更新
只使用cdn,不使用oss可以吗
- 可以只使用cdn,可以直接把静态资源上传到cdn
- 可以通过cdn做内容分发和存储
工具分析
web-vitals和Lighthourse
chrome插件:
-
Web Vitals
-
web-vitals是谷歌针对网页加载速度和体验所提出的一套指标, 如FP、FCP、LCP、FID、CLS、TBT、TTFB
-
通过此插件可以看到性能指标数据
-
-
Lighthourse
- chrome的性能分析工具,灯塔性能分析
- 通过Chrome浏览器的开发者工具,可以使用Lighthouse进行性能分析
- Lighthouse会生成详细的报告,包括页面加载性能、性能指标、以及会给出修改的建议
浏览器的performance
- 浏览器的performance是浏览器自带的性能工具
- 可以查看火焰图、帧率、主线程JS执行时间、渲染时间等
- 火焰图:用于展示页面加载过程中每个任务的耗时情况
- 帧率:指的是浏览器每秒渲染的帧数
网络层面优化
静态资源、体积大小、缓存
-
开启http2
- 多路复用可以同时发送多个请求和响应,可以并行的传输,解决队头阻塞的问题
- 头部压缩=>减少了数据传输的大小、二进制协议=>解析起来更高效
- 提高传输效率
-
开启gzip压缩
- nginx开启gzip、前端构建工具gzip
-
利用缓存,配合强缓存/协商缓存
-
DNS预解析优化
- link 标签的rel属性设置
dns-prefetch,提前解析域名对应的IP地址 - DNS解析 会进行解析ip地址 (解析过程中会进行查找,首先通过本地缓存 => 系统 => 本地映射 => 远程DNS源上查找),因此解析DNS过程中需要一定时间,所以就出现了DNS预解析,可以提前解析好ip地址
- link 标签的rel属性设置
-
CDN优化
- CDN缓存,设置合适的过期时间,减少不必要的请求。
- CDN可以通过配置,定期获取源服务器上的内容,来定期同步更新
- 如果数据已经过期,CDN可以发出回源请求,来拉取最新的数据
- 注意如果CDN还没过期,而又重新更新了内容,需要刷新CDN
- 刷新CDN
- 可以手动在阿里云上刷新CDN
- 可以通过脚本,在cicd时,当构建成功时,执行刷新CDN脚本
- 刷新CDN
- CDN缓存,设置合适的过期时间,减少不必要的请求。
-
http优化
- 减少http请求
- 利用缓存, 通过强缓存从本地缓存中获取资源
- 图片可以利用图片精灵雪碧图
- 接口的请求可以考虑合并 (这个需要和后端沟通)
- 小图片可以转换base64
- 减少http响应时间
- 针对于大数据接口请求,响应时间是比较慢,可以考虑分步去请求接口,减少响应时间
- 减少http请求
图片加载优化
加载的图片太多或者太大导致页面加载完成慢的问题;图片太多导致向服务器请求的次数太多,图片太大导致每次请求的时间过长,导致用户长时间等待
- 将图片放在CDN服务器上,加快图片资源的访问速度
- 配置图片缓存策略,利用缓存图片,直接缓存在浏览器本地,减少重复请求
- 图片懒加载
- 页面渲染过程中,图片不会一次性全部加载,会在需要的时候加载,滚动的时候加载
- 优化这个用户体验,一般会加上占位图
- 只有当资源即将进入用户视口(可视区域)时才真正加载,从而减少首屏加载时间、降低带宽消耗、提升整体用户体验
实现方案:
- 1、默认给图片src一个占位图
- 2、当图片进入可视区域,才把对应的图片链接赋值给src,才会进行加载图片
所有图片一次性加载,导致页面渲染慢原因分析:
-
浏览器对同一域名的并发请求有限制(通常为6-8个),大量请求会排队等待,导致后续资源(CSS、JS、字体等)也被延迟
-
首屏一次性发起所有图片的 HTTP 请求,会占用大量网络带宽。
-
会影响LCP指标(首屏最大内容绘制时间)
懒加载的优势
- 提升首屏速度:只加载视口内及附近的内容,用户打开页面时几乎立刻看到内容框架。
- 节省带宽:未滚动的图片完全不请求,特别对移动用户友好。
- 减少服务器压力:请求分散在用户滚动过程中,而不是集中爆发。
- 改善核心指标:LCP(Largest Contentful Paint)、FID、CLS 等 Web Vitals 指标都会受益