React不直接操作DOM,它在内存中维护一个快速响应的DOM描述,render方法返回一个DOM的描述(“虚拟DOM”),React能够计算出两个DOM描述的差异,然后更新浏览器中的DOM。这就是著名的DOM Diff。
组件更新特点:
(1)当一个组件的props或者state改变时,React通过比较新返回的元素和之前渲染的元素来决定是否有必要更新实际的DOM。当他们不相等时,React会更新DOM。(shouldComponentUpdate默认返回true)
(2)父组件更新默认触发所有子组件更新
1.React组件渲染机制
这是官网给出的React组件渲染机制描述图。绿色的表示不需要更新。
component-update.png
通过观察我们发现:
影响更新的条件主要有SCU(shouldComponentUpdate)及DOM DIff结果。
我们再来看看 组件触发更新的流程图:
render.png
通过上述流程图,再对比渲染的图解可以看到,React的性能瓶颈主要出现在生成DOM及DOM Diff的过程。如果进行性能优化,关键在:
shouldComponentUpdate 阶段判断,如果属性及状态与上一次相同,这个时候很明显UI不会变化,也不需要执行后续生成DOM,DOM Diff的过程了,可以提高性能。
DOM Diff 阶段优化,提高Diff的效率
2.如何提高组件的渲染效率
途径一:
子组件执行 shouldComponentUpdate 方法,自行决定是否更新。具体来说就是当一些属性或状态相等时,我们不去更新。
(2.1)使用严格全等
我们可以通过控制子组件的 shouldComponentUpdate( 接收两个参数,分别为待更新的属性及状态值) 从而控制是否渲染:
way1.png
但是这里有两个问题
(1)当你的组件变得更加复杂时,你使用===比较属性和值以判定是否需要更新组件,但是书写会异常麻烦。
(2)当属性和状态里出现引用类型(数组,对象),此时直接通过===比较是行不通的,因为对象的引用类型。我们知道===对于引用类型的比较是通过比较地址来决定是否相等。除非你让它指向一个新的对象(即使值相等)或值,它才会更新。
我们先来看看第二问题
shadllowCompare.png
当我们点击按钮是,label里面并不会增加“sed”,也就是this.state.label
和nextSate.label
是一样的。我们查看控制台就可以知道:
console.png
然后我们把代码中的注释语句去掉,再把var arr=this.state.label; arr[3]="sed";
注释掉。我们让arr指向一个全新的对象(值相同,值不相同更用说),再赋值给label,我们可以运行发现组件更新了。
这里我们就展示了使用===带来的副作用(虽然state:引用类型 已经改变,但组件不会更新),我们可以使用immutable.js来帮我们解决这个问题
(2.2)使用React.PureComponent
我们这里先来解决第一个问题,React提供了一个辅助对象来实现浅比较(shallowCompare)这种模式 - 继承自React.PureComponent。当组件更新时,如果组件的 props 和 state 都没发生改变,render 方法就不会触发,省去 Virtual DOM 的生成和比对过程,达到提升性能的目的。
way2.png
补充:React在之前版本提供了 PureRenderMixin 的mixin形式---react-addons-pure-render-mixin,也可以实现这样的功能。浅比较你可以简单看成===严格全等,但其实是===优化的结果,详情看你真的了解浅比较么?
PureComponent.png
当我们点击按钮是,label里面并不会增加“pattyzzh”,也就是我们上面的第二个问题依然存在。官网把这个问题归结为浅比较会忽略属性或状态突变的情况。其实以自己个人的理解就是引用带来的副作用。
这里的解决方案主要有:
深比较: 对应深拷贝(完完全全复制一份)类似,比较耗时,不推荐。
immutable.js:FaceBook官方提出的不可变数据解决方案,主要解决了复杂数据在deepClone和对比过程中性能损耗
(2.3)immutable.js不可突变的数据结构
Immutable Data 就是一旦创建,就不能再被更改的数据。对 Immutable 对象的任何修改或添加删除操作都会返回一个新的 Immutable 对象。Immutable 实现的原理是 Persistent Data Structure(持久化数据结构),也就是使用旧数据创建新数据时,要保证旧数据同时可用且不变。同时为了避免 deepCopy 把所有节点都复制一遍带来的性能损耗,Immutable 使用了 Structural Sharing(结构共享),即如果对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点则进行共享。
immutable.png
我们运行后会发现,正如上面提到对 Immutable 对象的任何修改或添加删除操作都会返回一个新的 Immutable 对象,所以shouldComponentUpdate中可以有效比较前后两次值是否相等。
途径二
给列表中的组件添加key属性(针对列表遍历类型,遍历时候增加唯一 key 属性值,对子组件进行唯一性识别,准确知道要操作的子组件,提高 DOM Diff 的效率。)
参考
React组件性能调优
如何有效地提高react渲染效率--深复制,浅复制,immutable原理
作者:pattyzzh
链接:https://www.jianshu.com/p/100a55978253