列表页性能优化实践

背景

小编在在项目中遇到了大数据量的列表出现严重卡顿的情况,其中比较明显的两个反馈:Table选中操作响应迟缓(3s以上),第二个是切换分支之后数据渲染的很慢(8秒以上,数据请求才50ms)。

对其历史代码排查发现在代码设计存在一些缺陷,在这里记录下,大家开发的时候注意规避。

性能优化过程

首先介绍下如何解决这个性能问题。

解决性能问题最关键一点就是如何量化性能指标,只靠自我感知很难明确的优化的效果以及中间的渲染过程,这里介绍下我是如何计算渲染各个阶段的耗时的。

Table 选中checkbox的选中效果展示的计时打点

上面有五个计时点,最终的触发顺序是 1 → 2 → 3 → 0 → 0 → 4 其中 3 → 0 → 0 → 4 的环节耗时比较久,大概在2s+,这里两次0-render是由于列表页的loading是基于 dva-loading 实现的,会触发一次render。

实际数据在第一次0-render就已经OK了,但是渲染过程比较慢。

这里有以下方面原因:

1、Table 在旧版本是不支持虚拟滚动的,且每次checkbox的状态变化会引起所有Table Row的重新渲染,且checkbox的选中动画也比较消耗性能,会导致响应缓慢,官方Github Issue中有介绍github.com/ant-design/...

2、代码设计的内部state 直接存储了列表原数据,并且选中selctedKey存储的是原数据,反选也存储原数据,不是row-key,Table组件接受的selectedKey也要经过useMemo进行map提取Key,计算复杂,数据体积大,给React VirtualDom 的diff过程带来了很大的压力

3、dva-loading是基于effect 接口请求实现的,是个root redux store的状态,会引起大量组件的重绘,产生很多无用性能开销

切换分页数据渲染很慢

上述八个计时点,7 → 8 耗时大,和第一个问题的原因是一样的,其中比较特殊的是 4 → 5 的耗时也有1秒多,经过排查发现以下性能原因:

1、目前Redux的全局store十分冗余,导致其diff过程变长

2、useEffect 和 useMemo 的数据依赖存在交叉,且diff计算过大,出现useEffect和useMemo多次串行渲染的情况,没能实现状态合并渲染

3、onPageChange和选中态实时同步到了父组件,导致父组件也频繁render,增加diff复杂度

优化总结

1、针对 antd 本身的性能缺陷,官方推荐升级antd Table 4版本,支持虚拟渲染;社区的大佬推荐关闭checkbox的过渡动画,或者直接使用 原生 < input type='checkbox' /> 替换checkbox,避免选中态变化引起Table的重绘。这两个方式成本较大

2、将Table 的数据从 Redux store 迁移到Table组件内部,列表数据并不是共享数据,因此没有必要放到全局store中,导致store数据变化传递到组件内部的链路长且耗时长。

3、Table 的loading交互不要使用dva-loading实现,使用Table-loading api即可,基于redux action 在 Table 组件内部维护即可,避免Table分页变化,引起一系列父组件的render。

css 复制代码
listApplication: generateEffect('departCooperation/listApplication', { loading: false }),

4、由于数据请求迁移到内部,因此其分页数据之后的数据变化就不需要依赖useEffect和useMemo了,直接在promise.then之后直接计算所有依赖数据,避免出现useEffect和useMemo出现交替串行渲染的问题

5、精简useState维护的数据,移除 includeRows、excludeRows、defaultSelectedRows 原数据存储,改用projectId存储,精简useMemo和useMemo计算逻辑,移除Map类型的数据维护

6、将提交按钮从容器组件移动到Table组件内,将提交处理函数从容器组件传递到Table组件,避免value-change无用频繁触发。

规范沉淀

1、列表类数据禁止使用redux Store存储,非全局核心复用数据不能使用 redux store

2、数据state的数据量要控制大小,尽可能使用常规 JavaScript 的数据类型,尽可能使用非引用类型数值

3、组件封装注意数据流的管理,value-change 模式是指子组件不具有主动调用的能力,数据实时同步,十分适合UI复用;handler-props则是子组件控制调用时机,主动调用通过props传递进来handler函数逻辑,并且补全handler内部参数,这些参数一般为子组件内部状态,思想贴近函数柯里化,且更加容易复用逻辑。这两种模式各有优缺点。

这里列举下使用场景 value-chang模式适合UI组件,即组件是最小粒度的元素,例如各大UI库;handler-props 则更加使用业务组件的封装,可以屏蔽内部逻辑细节,只关注在handler的处理逻辑的适配,明确数据含义。

4、关注 antd 组件提供的各种API参数,除非组件提供的API完全不能满足需要,否则必须使用官方API实现,因为这些规范是最具有共识的,只要大家使用这些API,其代码风格和设计会自然而然保持一致的,而且十分有利于后续版本的升级,无需额外的改造成本。

相关推荐
崔庆才丨静觅43 分钟前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60611 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了2 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅2 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅2 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅2 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment3 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅3 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊3 小时前
jwt介绍
前端
爱敲代码的小鱼3 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax