June 2, 2023
By: 王云腾

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官网功能和案例介绍的很详细

总结

整体优化思路

  1. antdTable改用虚拟列表.
  2. 优化全屏额外花销.
  3. 取消无效渲染.

这个思路同样可以运用到chartSwitch其他的数据展示组件中.

效果(来自同一开发机Chrom性能监测器)

宽带中频信道化优化前
宽带中频信道化优化后
信号控守优化前
信号控守优化后
融合电磁环境识别

Tags: react