列表页性能优化实践

背景

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

相关推荐
计算机魔术师3 分钟前
智谱开源 GLM-5.3 模型权重,主打智能体编程与网络防御
前端
百变梦仔5 分钟前
多个 Skill 同时出现时,Codex 前端任务我会先定优先级
前端
IT_陈寒18 分钟前
SpringBoot自动配置失效?这个隐式依赖坑了我三天
前端·人工智能·后端
码视野24 分钟前
基于 Spring Boot + Vue3 的【城市雨污水管网液位淤积溯源与立交桥下穿隧洞防汛排涝智控中台】设计与实现(含PRD/三端高保真源码/大屏)
java·前端·人工智能·spring boot·后端
雪芽蓝域zzs24 分钟前
第五节:Vue‑Router4 路由配置,布局嵌套路由
前端·javascript·vue.js
雪芽蓝域zzs27 分钟前
第十二节:完整用户管理 CRUD 页面(表格分页、新增 / 编辑弹窗、删除、Mock 接口)
前端·javascript·vue.js
深念Y38 分钟前
NativeScript 移动端开发踩坑记录
前端·ui·vue·安卓·移动端·native·原生
HackTwoHub43 分钟前
Butter_Cookie 黄油曲奇|浏览器渗透插件,云存储检测、XSS、SQL 注入一站式 Web 安全测试工具
前端·sql·安全·web安全·网络安全·自动化·xss
2601_962066231 小时前
golang超详细基础入门教程_golang教程
前端·python·golang
单线程121381 小时前
从案例分析 Vue3 Tokenizer 源码二
前端·javascript·vue.js