列表页性能优化实践

背景

小编在在项目中遇到了大数据量的列表出现严重卡顿的情况,其中比较明显的两个反馈: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,其代码风格和设计会自然而然保持一致的,而且十分有利于后续版本的升级,无需额外的改造成本。

相关推荐
To_OC1 小时前
别再串行写 await 了,Promise.all 才是并行请求的正确打开方式
前端·javascript·promise
vipbic1 小时前
中后台越做越乱后,我用插件化把它救回来了
前端·vue.js
Hyyy1 小时前
Computer Use 适合做什么,不适合做什么——一次真实使用后的思考
前端
小和尚同志2 小时前
前端 AI 单元测试思考与落地
前端·人工智能·aigc
invicinble3 小时前
c端系统,其实更像一个信息展示平台
前端
李姆斯4 小时前
管理是否可以被完全量化
前端·产品经理·团队管理
名字还没想好☜4 小时前
Next.js 中间件实战:鉴权、重定向与 A/B 分流
开发语言·前端·javascript·中间件·react·next.js
广州灵眸科技有限公司5 小时前
瑞芯微RV1126B开发板(EASY-EAI-PI2) INI文件操作
java·前端·javascript·网络·人工智能
江华森5 小时前
Web 开发基础:HTTP 与 REST
前端·网络协议·http
IT_陈寒6 小时前
Redis的KEYS命令把我搞崩溃了,改用SCAN才活过来
前端·人工智能·后端