文章目录
React性能优化SCU
React更新机制
我们在前面文章已经讲解过React的渲染流程:
那么React的更新流程呢?
React在props或state发生改变时,会调用React的render方法,会创建一颗不同的树。
React需要基于这两颗不同的树之间的差别来判断如何有效的更新UI:
如果一棵树参考另外一棵树进行完全比较更新,那么即使是最先进的算法,该算法的复杂程度为 O(n²),其中 n 是树中元素的数量;
https://grfia.dlsi.ua.es/ml/algorithms/references/editsurvey_bille.pdf;
如果在 React 中使用了该算法,那么展示 1000 个元素所需要执行的计算量将在百万的量级范围;
这个开销太过昂贵了,React的更新性能会变得非常低效;
于是,React对这个算法进行了优化,将其优化成了O(n),如何优化的呢?
同层节点之间相互比较,不会垮节点比较;
不同类型的节点,产生不同的树结构;
开发中,可以通过key来指定哪些节点在不同的渲染下保持稳定;
当子元素拥有 key 时,React 使用 key 来匹配原有树上的子元素以及最新树上的子元素:
将key为333的元素插入到最前面的位置即可;
在这种场景下,key为111和222的元素仅仅进行位移,不需要进行任何的修改;
key的注意事项:
key应该是唯一的;
key不要使用随机数(随机数在下一次render时,会重新生成一个数字);
使用index作为key,对性能是没有优化的;
render函数被调用
演示render函数调用我准备使用如下的组件嵌套关系
此时App中有两个数据, message和conter, 并且将message传给Home组件展示, 将conter传给About组件展示; 切App组件有两个按钮, 分别用于修改message和conter数据的, 代码如下
exportclassAppextendsComponent{constructor(){super()this.state ={message:"Hello World",conter:100}}changeText(){this.setState({message:"你好啊"})}changeNum(){this.setState({conter:this.state.conter +1})}render(){
console.log("App render")const{ message, conter }=this.state
return(<div><h2>{message}</h2><button onClick={()=>this.changeText()}>修改文本</button><h2>{conter}</h2><button onClick={()=>this.changeNum()}>修改数字</button><Home message={message}/><About conter={conter}/></div>)}}
那么此时, 我们点击任意一个按钮, 修改了state中的数据, 就会重新调用App组件的render函数
而当App的render函数被调用时,所有的子组件的render函数都会被重新调用;
并且当我点了一次修改文本按钮时, state中的message已经变成了"你好啊", 当我再次点击修改文本按钮时, state中的message依然是"你好啊", 并没有发生变化, 但是App组件的render函数依然会重新执行, 并且它的子组件Home和About中的render函数也会重新执行
**那么,我们可以思考一下,在以后的开发中,我们只要是修改了 App中的数据,所有的组件都需要重新render,进行diff算法,
性能必然是很低的
**:
事实上,很多的组件没有必须要重新render;
它们调用render应该有一个前提,就是依赖的数据(state、props)发生改变时,再调用自己的render方法;
如何来控制render方法是否被调用呢?
前面我有讲到一个生命周期函数, 通过
shouldComponentUpdate
函数方法可以控制render方法是否被调用;
例如App组件中, 我们调用shouldComponentUpdate生命周期函数, 当message和conter改变时, 才会执行render函数, 否则不执行
shouldComponentUpdate(newProps, newState){if(this.state.message !== newState.message ||this.state.conter !== newState.conter){returntrue}returnfalse}
我们同样可以在Home组件和About组件调用shouldComponentUpdate生命周期函数, 进行限制
// Home组件exportclassHomeextendsComponent{shouldComponentUpdate(newProps, newState){// 当message改变才会调用render函数if(this.props.message !== newProps.message)returntruereturnfalse}render(){
console.log("Home render")const{ message }=this.props
return(<div><h2>{message}</h2></div>)}}
// about组件exportclassAboutextendsComponent{shouldComponentUpdate(newProps, newState){// 当conter改变才会调用render函数if(this.props.conter !== newProps.conter)returntruereturnfalse}render(){const{ conter }=this.props
console.log("About render")return(<div><h2>{conter}</h2></div>)}}
此时在App组件中点击修改文本按钮, 由于message值发生变化, 只有App组件和Home组件的render函数会被重新调用, About组件不会; 点击修改数字按钮, App组件和About组件的render函数会被调用, Home组件不会, 这样我们就对性能进行了优化
PureComponent
如果所有的类,我们都需要向上面代码一样手动来实现 shouldComponentUpdate,那么会给我们开发者增加非常多的工作量, 况且上面代码中还只是修改了两个state中的数据, 如果数据更多我们的工作量将更大。
我们来设想一下shouldComponentUpdate中的各种判断的目的是什么?
props或者state中的数据是否发生了改变,来决定shouldComponentUpdate返回true或者false;
事实上React已经考虑到了这一点,所以React已经默认帮我们实现好了,如何实现呢?
将class继承自Component修改为PureComponent就可以了。
exportclassHomeextendsPureComponent{render(){const{ message }=this.props
return(<div><h2>{message}</h2></div>)}}
高阶组件memo
目前我们是针对类组件可以使用PureComponent,那么函数式组件呢?
事实上函数式组件我们在props没有改变时,也是不希望其重新渲染其DOM树结构的
针对于函数组件, 我们需要使用一个高阶组件memo:
使用memo对函数组件进行包裹, memo调用会返回一个新的组件, 返回这个组件后, 函数组件也会和累组件的PureComponent是一样的效果
// 导入memoimport{ memo }from"react";// 使用memo会返回一个新的组件const Profile =memo(function(props){return(<div><h2>{props.message}</h2></div>)})exportdefault Profile
版权归原作者 蓝桉cyq 所有, 如有侵权,请联系我们删除。