React组件性高效渲染

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.labelnextSate.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原理

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 说在前面 关于 react 的总结过去半年就一直碎碎念着要搞起来,各(wo)种(tai)原(lan)因(le)。心...
    陈嘻嘻啊阅读 6,917评论 7 41
  • 1. 前言 在 React 中,一切皆是组件,因此理解组件的工作流与核心尤为重要。我们有多种创建组件的方式(不仅 ...
    cbw100阅读 3,661评论 0 10
  • 3. JSX JSX是对JavaScript语言的一个扩展语法, 用于生产React“元素”,建议在描述UI的时候...
    pixels阅读 2,886评论 0 24
  • 原教程内容详见精益 React 学习指南,这只是我在学习过程中的一些阅读笔记,个人觉得该教程讲解深入浅出,比目前大...
    leonaxiong阅读 2,860评论 1 18
  • 听说生命是一条长长的河 传说河里只有鱼和水滴 水滴日夜不停 奔向远方 鱼儿终日无忧无虑在水中嬉戏玩耍 它们被称为这...
    追梦人hjw阅读 191评论 2 2