列表页性能优化实践

背景

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

相关推荐
学代码的小前端几秒前
0基础学前端-----CSS DAY13
前端·css
css趣多多1 小时前
案例自定义tabBar
前端
姑苏洛言3 小时前
DeepSeek写微信转盘小程序需求文档,这不比产品经理强?
前端
林的快手3 小时前
CSS列表属性
前端·javascript·css·ajax·firefox·html5·safari
匹马夕阳3 小时前
ECharts极简入门
前端·信息可视化·echarts
API_technology3 小时前
电商API安全防护:JWT令牌与XSS防御实战
前端·安全·xss
yqcoder4 小时前
Express + MongoDB 实现在筛选时间段中用户名的模糊查询
java·前端·javascript
十八朵郁金香4 小时前
通俗易懂的DOM1级标准介绍
开发语言·前端·javascript
m0_528723815 小时前
HTML中,title和h1标签的区别是什么?
前端·html
Dark_programmer5 小时前
html - - - - - modal弹窗出现时,页面怎么能限制滚动
前端·html