React项目 数据展示页面的性能优化
当时正好忙完手里的活听到小思哥和负责人在讨论,有一个页面的性能贼差,数据一多卡的动不了,就和小思哥讨论了下,发现这个页面使用的antd的table组件,改为虚拟列表后好了很多但是看cpu占用度还是居高不下,正好小思哥去忙后端我就把这个任务揽了下来。
全屏优化
在和小思哥的讨论中得知现在的全屏组件是使用visibility:none/visible;来控制全屏组件的显示。但是这个方案是占位隐藏,虽然看不到,但还是会生成dom,更新dom.
使用display控制也有同样的问题,而且display为none加载,初始化时offsetWidth获取不到容器宽高,导致显示有问题.可以在性能监测中看到是否开启全屏cpu占用度并没有变化.
在vue中提供了两个指令来控制显示隐藏: v-show控制display值, v-if控制组件的装载和销毁.
在react中可以使用条件渲染来选择性地渲染JSX.由此封装了如下的组件
export const FullScreenContainer = (prop) => {
const { isShow } = prop
const render = () => {
return (
prop.children
)
}
return (
<>
{
isShow && (
<FullScreen id="full_screen_dom">
{
render()
}
</FullScreen>
)
}
</>
)
}
组件内逻辑没有与外部产生关联.只需要在使用时将全屏加载的内容利用组件children传进来.props传入是否显示全屏的状态来控制内部逻辑是否开始执行.这样我们不开启全屏时比之前减少了所有全屏的花销.
取消无效刷新
在测试上面全屏组件的加载状况时添加了一个log.发现数据来了一包,控制台输出了十一次.
组件结构如下:
我们在chartSwitch组件中维护了订阅的socket,内部使用了公共的组件四个图标和一个列表,他们的状态共由十一个useState去控制.当有数据返回时在chartSwitch中处理后去更新useState,从而渲染页面.
但是react的数据更新有一个特点,当父组件状态变化时父组件以及其内部的子组件都会重新渲染.所以导致了单个全屏组件中的log输出了11次.
这时小思哥来询问进度如何,和他汇报了这个情况后,给我推荐了一个"神器": React Developer Tools.
React Developer Tools
在控制台分为两个模块:Components, Profiler panels.
性能分析模块收集有关渲染的每个组件的计时信息,以便识别React应用程序中的性能瓶颈.
正好可以分析我们的项目.

在记录中可以明确的看到每个子组件确实渲染了11次.但是每个子组件用到的只用一个到两个状态.而且我们的虚拟列表的时间花销很大.我就想着能不能让子组件在其需要的状态更新时才更新,优化效果想想就会很不错.
useMemo与useCallback
这时候就想到了react提供的hook,useMemo(memo)就是用来缓存计算结果的. 他的功能之一就是跳过组件的重新渲染.每次组件更新时会先判断依赖项是否能通过比较(object.is()),如果比较相等则直接取缓存值,不等再重新计算.需要注意的是如果依赖项有一个函数(引用数据类型),则需要使用useCallback将函数包一下,否则不会通过比较. (useCallback会缓存fn的内容(引用数据类型比较内存地址))
使用之后的效果:

可以看到虚拟列表组件只有在两个依赖项更新时才会重新渲染.符合预期.但是只对table组件进行了缓存并没有对四个chart做缓存,一个原因是做了缓存后优化的程度很有限,另一个是了解到在react之前的版本每一个组件是默认带memo的,而判断是否应用缓存也有额外的花销.
useMemo与useCallback在react官网功能和案例介绍的很详细
总结
整体优化思路
- antdTable改用虚拟列表.
- 优化全屏额外花销.
- 取消无效渲染.
这个思路同样可以运用到chartSwitch其他的数据展示组件中.
效果(来自同一开发机Chrom性能监测器)




