在react项目中使用shouldComponentUpdate方法进行组件性能优化
时间:3年前 作者:庞顺龙 浏览:4739 [站内原创,转载请注明出处]
在react项目中使用shouldComponentUpdate方法进行组件性能优化,前半部分主要是思路和官方方法解释,最后两张大图为测试对比结果。
react的渲染流程主要是
初始化组件
该阶段会执行组件及其所有子组件的render方法,从而生成第一版的虚拟dom。
组件更新渲染
组件的props或者state任意发生改变就会触发组件的更新渲染。默认情况下其也会执行该组件及其所有子组件的render方法获取新的虚拟dom。
接下来我们聊聊组件更新渲染的性能问题。
react组件更新流程
通过上面分析可以知道组件更新具体过程如下:
执行该组件及其所有子组件的render方法获取更新后的虚拟DOM,即re-render,即使子组件无需更新。
然后对新旧两份虚拟DOM进行diff来进行组件的更新
在这个过程中,可以通过组件的shouldComponentUpdate方法返回值来决定是否需要re-render。
react的整个更新渲染流程可以借用一张图来加以说明:
默认地,组件的shouldComponentUpdate返回true,即React默认会调用所有组件的render方法来生成新的虚拟DOM, 然后跟旧的虚拟DOM比较来决定组件最终是否需要更新。
性能瓶颈分析
借图说话,例如下图是一个组件结构tree,当我们要更新某个子组件的时候,如下图的绿色组件(从根组件传递下来应用在绿色组件上的数据发生改变):
理想情况下,我们只希望关键路径上的组件进行更新,如下图:
但是,实际效果却是每个组件都完成re-render和virtual-DOM diff过程,虽然组件没有变更,这明显是一种浪费。如下图黄色部分表示浪费的re-render和virtual-DOM diff。
根据上面的分析,react的性能瓶颈主要表现在:
对于props和state没有变化的组件,react也要重新生成虚拟DOM及虚拟DOM的diff。
这个时候,就是shouldComponentUpdate上场的时候了。
shouldComponentUpdate(nextProps, nextState){ return !isEqual(nextProps, this.props) || !isEqual(nextState, this.state) }其中,isEqual方法为判断两个对象是否相等(指的是其对象内容相等,而不是全等)。
通过显示覆盖shouldComponentUpdate方法来判断组件是否需要更新从而避免无用的更新,但是若为每个组件添加该方法会显得繁琐,好在react提供了官方的解决方案,具体做法:
方案对组件的shouldComponentUpdate进行了封装处理,实现对组件的当前属性和状态与上一次的进行浅对比,从而决定组件是否需要更新。
react在发展的不同阶段提供两套官方方案:
PureRenderMin
一种是基于ES5的React.createClass创建的组件,配合该形式下的mixins方式来组合PureRenderMixin提供的shouldComponentUpdate方法。当然用ES6创建的组件也能使用该方案。
import PureRenderMixin from 'react-addons-pure-render-mixin'; class Example extends React.Component { constructor(props) { super(props); this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this); }PureComponent
该方案是在React 15.3.0版本发布的针对ES6而增加的一个组件基类:React.PureComponent。这明显对ES6方式创建的组件更加友好。
import React, { PureComponent } from 'react' class Example extends PureComponent { render() { // ... } }需要指出的是,不管是PureRenderMin还是PureComponent,他们内部的shouldComponentUpdate方法都是浅比较(shallowCompare)props和state对象的,即只比较对象的第一层的属性及其值是不是相同。例如下面state对象变更为如下值:
state = { value: { foo: 'bar' } }因为state的value被赋予另一个对象,使nextState.value与this.props.value始终不等,导致浅比较通过不了。在实际项目中,这种嵌套的对象结果是很常见的,如果使用PureRenderMin或者PureComponent方式时起不到应有的效果。
虽然可以通过深比较方式来判断,但是深比较类似于深拷贝,递归操作,性能开销比较大。
为此,可以对组件尽可能的拆分,使组件的props和state对象数据达到扁平化,结合着使用PureRenderMin或者PureComponent来判断组件是否更新,可以更好地提升react的性能,不需要开发人员过多关心。
案例分析
TimeLineView 列表页组件面
TimeLineEventView 列表item组件页面
说明:列表页面 数据list.map(TimeLineEventView)
现有一个列表页面,滚动加载下一页,每个项目中均有一个点赞功能,按照逻辑我们一般是在列表jsx页面通过数据list.map(页面item方法)来实现,通过页面state来处理点赞按钮的样式切换:
我们不对列表item进行shouldComponentUpdate处理,下面通过Perf工具来检测当我们点击某一个item赞功能的时候,通过Perf输出页面性能检测结果:
如上图的Print Wasted 模块,我们可以看到当前列表页面,我们点击某一个item的赞功能,页面会产生将近20ms的性能损耗,主要就是item组件 TimeLineEventView 被重新渲染了17次,而这17次都是没有任何变化的item产生的。
然后我们改造TimeLineEventView页面,增加shouldComponentUpdate处理:
shouldComponentUpdate(nextProps, nextState) { if (this.props.IsPraised == nextProps.IsPraised) { return false; } return true; }上面的代码就是充写了react默认返回true的shouldComponentUpdate方法,通过逻辑可知,只有被点击的item组件才会返回true,其他均为false,这样就会实现非相关组件不会重新render渲染,改造完毕后,我们再次通过Perf工具进行检测测试,结果如下:
继续看上图的Print Wasted 模块,可以看到,已经没有 TimeLineEventView 相关的组件渲染损耗记录了,说明我们优化成功,目前的渲染损耗在1ms左右,虽然从 20ms 到 1ms并不会对用户产生很大的感知,但是对于滚动加载列表来说,如果数据结构复杂,页面数据量达到几百条,那么损耗的性能可能就是7.8百ms,对于页面来说就会很明显的卡顿等,所以,对组件进行足够、合理的扁平化拆分就显得尤为必要了。
参考:
http://www.cnblogs.com/wonyun/p/6804952.html
http://benchling.engineering/deep-dive-react-perf-debugging/
https://segmentfault.com/a/1190000006741060
React Perf chrome插件地址:
https://chrome.google.com/webstore/detail/react-perf/hacmcodfllhbnekmghgdlplbdnahmhmm
内容均为作者独立观点,不代表八零IT人立场,如涉及侵权,请及时告知。
转载请注明:文章转载自-八零IT人 [http://www.80iter.com]
本文标题:在react项目中使用shouldComponentUpdate方法进行组件性能优化
本文地址:http://www.80iter.com/blog/1512370656110845

手机扫码阅读
热门相关
