React组件性高效渲染

作者: 卓三阳 | 来源:发表于2018-05-11 22:55 被阅读1384次

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组件性高效渲染

    React不直接操作DOM,它在内存中维护一个快速响应的DOM描述,render方法返回一个DOM的描述(“虚拟D...

  • React纯组件渲染性能反模式

    React纯组件渲染性能反模式 React纯组件的渲染可以非常高效,但是需要用户将其数据作为不可变的对象,才能正常...

  • 关于React组件

    渲染React组件 通过ReactDOM直接渲染 通过React.Component创建组件再通过ReactDOM...

  • React Native之React整理(一)

    概述 当数据改变时,React将高效的更新和渲染需要更新的组件。声明性视图使你的代码更可预测,更容易调试。 构建封...

  • React代码规范

    React代码规范 components 展示性组件里面一般存放ui组件,负责组件的外表,也就是组件如何渲染,具有...

  • 面试题

    React组件的渲染流程是什么? 使用 React.createElement或 JSX编写 React组件,实际...

  • React系统性学习(下)

    $ 前言   在《React系统性学习(上)》中我们主要学习了 什么是React JSX语法 元素渲染 组件(Co...

  • 【译】了解React源代码-UI更新(DOM树)9

    【译】了解React源代码-初始渲染(简单组件)1【译】了解React源代码-初始渲染(简单组件)2【译】了解Re...

  • 【译】了解React源代码-初始渲染(类组件)5

    【译】了解React源代码-初始渲染(简单组件)1【译】了解React源代码-初始渲染(简单组件)2【译】了解Re...

  • 【译】了解React源代码-初始渲染(类组件)4

    【译】了解React源代码-初始渲染(简单组件)1【译】了解React源代码-初始渲染(简单组件)2【译】了解Re...

网友评论

    本文标题:React组件性高效渲染

    本文链接:https://www.haomeiwen.com/subject/hjyqdftx.html