背景介绍
在React开发中,我们会经常运用各种方式去优化性能。比如利用PureComponent、React.memo等方式去跳过一些不必要的组件更新、利用useMemo、useCallback实现稳定的props值、状态下放缩小状态影响的范围等等。那么我们该从何做起呢,如何知道自己的项目哪些地方需要去做此类优化呢,或者说借助一定的方式优化之后,如何让优化效果可是化呢,到底是自己看到的结果才更让人放心一些,哈哈哈。借助React Profiler(以下简称RP)你就可以很轻松办到。
准备工作
安装及配置
首先你需要通过Chrome Store去安装RP扩展程序。在16.5+以上的React项目中,打开你的控制台,你可以看到以下画面:
点击右上角设置的按钮,可以对该插件进行简单的配置,这里你选择Profiler,然后选中第一条“Record why each component rendered while profiling.”记录profiling过程中为什么组件会渲染的原因。第二条就是过滤掉commits时间在多少ms以下的记录,看你个人喜好情况。
如何使用
你可以点击蓝色按钮,开始录制,或者点击旁边的刷新按钮,重刷你的网站然后直接开始录制
开启录制之后蓝色开始按钮变成了红色,你可以随意进行页面中你想要测试的交互,然后点击该红色按钮,录制结束,这时候你可以看到如下所示的画面
Profile过程说明
上图中标注了四个部分,是我们进行性能优化分析的关键:
- 图形选择:分为火焰图(Flame Chart)和排名图(Ranked Chart)两部分,点击不同的图标下面图像区域会进行相应的切换,默认我们采用火焰图模式,后面会对排名图进行简单说明;
- 图像区域:该区域是对React一次Commit过程中各个组件的渲染情况的详细描述。每一个横条代表了一个组件,从上到下对应根组件到子组件(from root to leaves)。从宽度和色深两个维度来对渲染过程进行了记录,宽度越宽表明该组件及其子组件渲染时间越长。颜色从黄色到绿色渐变,色值越深(黄)表明该组件自身渲染的时间越长(总时间是组件自身和组件子组件渲染的时间之和),反之,越浅(绿)表明组件自身的渲染时间占比越少。不用说,肯定是越绿性能越好呗。
也会出现灰色和灰色格子这两种颜色,灰色代表组件本身没有渲染,但是组件的子节点产生了渲染,此时这个组件是在整个React的渲染路径上。灰格代表组件不仅没有渲染,甚至都不在渲染路径上,没有任何子组件渲染。
3. 条形图区域:每一根条代表了一次React Commit,条越高表明此次commit花的时间越长,同样越黄性能越差,越绿性能越好。选中的条会以蓝色高亮,此时下面图像区域和右边说明区域会相应的变化;
4. 信息区域:某次React Commit的详细信息说明,当你点击图像区域的某一个横条(选中某个组件),该区域则变成了对当前组件的详细说明。
切换到排名图模式,图像区域如下所示:
在这种模式下,每一个横条不再包含子组件的渲染时间,只是记录自身的渲染时间。时间耗时最长的位置在最上方,从上到下,从繁至简,一目了然。
案例分析
说一百遍不如实际操作一遍,我们借助一个简单的案例来分析下性能,并浅浅的优化下,代码结构大致如下:
import { useState } from "react";
import Search from './search';
import List from './list';
import Others from './others';
const View = props => {
const [lists, setLists] = useState(new Array(20).fill(undefined).map(_ => Math.random().toString()));
const [searchText, setSearchText] = useState('');
const handleSeach = (value) => {
setSearchText(value)
}
return <div>
<h1>Hello, Profiler</h1>
<button onClick={() => setLists(prev => prev.concat(Math.random().toString()))}>修改数据源</button>
<Others data={lists} />
<Search handleSeach={handleSeach} />
<List data={lists.filter((item) => item.indexOf(searchText) !== -1)} />
</div>
}
export default View;
比较简单,大概就是index作为一个中心,引入了各个组件,然后其中有一个筛选组件,会根据搜索的信息进行数据过滤显示过滤后的数据。我们简单的进行一些数据搜索,比如123,然后有删除,通过RP查看下页面运行情况:
在搜索过程中,Search组件本身是没有状态变化的,而且它没有子组件参与渲染,但是它自身在不断的渲染,从上图中我们可以看到说明的是,handleSearch这样一个Props里面的属性在变化,回到代码,果然在每次父组件渲染过程中,相当于重新声明了一个函数
const handleSeach = (value) => {
setSearchText(value)
}
这样才导致了Search组件也不停在在参与渲染,简单的做下优化:
const handleSeach = useCallback((value) => {
setSearchText(value)
}, [])
//或者
<Search handleSeach={setSearchText} />
使用useCallback缓存住handleSeach函数,或者直接使用setSearchText,再查阅我们发现Search组件不会再因为Props的change而产生渲染。当然里面还有一些其他的“坑”可以被优化的,小伙伴们看出来了么,可以拉下代码试试吧。
写在最后
借助RP我们可以清晰的知道各个组件的渲染情况,哪个区域渲染性能较差可以说一目了然。本文给的列子不是那么的合适,只是为了说明可以使用RP来进行分析临时起意,哈哈哈,看了本篇关于RP的使用之后小伙伴是不是像拿自己之前的项目跃跃欲试呀,还在等什么呢,快行动起来吧。