记录一次组件性能优化的解决过程
一、背景
项目迭代期间,有个比较常用的React组件发现有性能问题,leader叫我看看。开始还是比较紧张的,因为这是公司平台的一个基础组件,各个团队都有可能会用,有点担心给改坏了,性能问题给改成了问题。这个组件是一个类似穿梭框的组件,将左侧的待选数据选择增加到右侧的已选数据。
二、问题记录
在点击单个数据时,只有一条数据增加到右侧选中区,体验良好。 在点击全选按钮时,将左侧列表全部数据增加到右侧选中区。在数据量为300左右时,耗时大概是90s,迟滞感明显。 这个性能问题可以稳定复现。
三、问题排查
- 在源码中找到 全选操作的回调函数,按照全选操作执行的调用链依次排查。 (具体的代码就不贴出来了,抽象成逻辑来分析)。
-
点击全选按钮,调用 selectAll 函数
- 循环遍历 selectAllData ,将数组的每一项 作为参数 传入 B 函数,向外部抛出
-
调用 B 函数
-
调用 C 函数
-
判断传入的数据项是否可以增加
- 可以增加,全选选中的数据项 和 已选中的数据 合并作为 新的已选中的数据,并调用setState重新设置已选中的数据。
- 不可以增加,不作操作。
-
-
调用 C 函数
在全选操作执行的调用链中,可以发现,主要的逻辑都集中在 B 函数中,全选时选中的数据有多少项,B 函数就会执行多少次。
每次执行 B 函数,都会调用 C 函数,都有可能调用setState修改状态并进行重新渲染,这就导致全选操作的耗时随着数据量线性增长。
导致性能问题的原因主要有两个:
-
新增数据的每一项都会调用 appendItem 函数,有可能修改state进行重新渲染
-
每次调用 appendItem 函数都会调用 resetSearch 函数 来重置搜索相关的逻辑
为什么其他情况没有暴露出性能问题:
在点击单个数据进行选择的时候,只会调用一次 B 函数,单次操作耗时比较少,用户基本无感知。
四、优化方案
优化思路:
针对导致性能问题的主要原因,在结合具体的需求和交互,做出以下优化
- 全选时,先将 全选选中的数据 和 已选择的数据 进行 去重合并,再用得到的新数据更新 state。
(将多次的setState合并成一次。)
- 在设置完新的state之后,再调用 C 函数。
(从交互上来说,原有交互是在点击某个选项时会新增到已选择数据并重置特定逻辑,在统一设置完新的state后再重置特定逻辑符合原有的交互。)
具体实现:
新增了一个props onSelectAll,在点击全选时将全选全选选中的数据整体向外抛出
五、优化结果
指标 | 优化前 | 优化后 | 对比 |
---|---|---|---|
数据量 | 278条 | 278条 | |
耗时 | 90s | 2s | 耗时减少97%(粗略) |
Google Chromeperformance | ![]() | ![]() |
从 performance 的分析来看。
CPU的图形分布中,script 的执行时间占比较大,可以初步判断是script的执行消耗了大量的资源。
火焰图中存在大量的近似的起伏,有可能是大量的重复调用。
六、总结
1. 性能问题的排查思路
这里主要是指代码逻辑层面
-
找到触发性能问题的场景,依照操作执行的调用链依次排查
-
性能问题分两种:
-
不可用(资源耗尽,爆栈):是否存在无限循环,循环调用......
-
可用(只是单纯的慢):代码逻辑是否合理,是否存在冗余操作,执行和渲染,重复的操作在逻辑上是否可以进行合并......
-
2. 工具
- 朴实而万能的 console
- Google Chrome performance
- 好像还有一些专业的性能测试工具,但是还不太会用哈哈哈
转载自:https://juejin.cn/post/7155071103696633886