React是一个专一于UI层的框架,它使用虚拟DOM技术,以保证它UI的高速渲染;使用单向数据流,所以它数据绑定更加简单;那么它内部是如何保持简单高效的UI渲染呢?这种渲染机制有可能存在什么性能问题呢?javascript
原文连接120java
React不直接操做DOM,它在内存中维护一个快速响应的DOM描述,render方法返回一个DOM的描述,React可以计算出两个DOM描述的差别,而后更新浏览器中的DOM。这就是著名的DOM Diff
。react
就是说React在接收到属性(props)或者状态(state)更新时,就会经过前面的方式更新UI。因此React整个UI渲染是比较快的。可是这里面可能出现的问题是:git
假设咱们定义一个父组件,其包含了5000个子组件。咱们有一个输入框输入操做,每次输入一个数字,对应的那个子组件背景色变红。github
<Components> <Components-1 /> <Components-2 /> <Components-3 /> ... <Components-5000 /> </Components>
这样咱们输入数字1,则子组件1背景色变化,可是在这个过程当中,全部的子组件都进行了从新渲染,致使总体渲染变慢。形成这种现象的缘由是 React中父组件更新默认触发全部子组件更新
。(具体例子见文章后demo连接)浏览器
同时,咱们常常在遍历列表元素时候会遇到这样的提示:性能优化
Warning: Each child in an array or iterator should have a unique "key" prop.
这就是咱们今天要讨论的两个性能优化点:数据结构
咱们利用 react-addons-perf
进行性能检测。引入方法以下:app
import Perf from 'react-addons-perf' window.Perf = Perf // 挂载到全局变量方便使用
检测方法,在浏览器控制台输入以下命令:框架
控制台会以表格的形式展现出结果:
上图记录了每一个组件的执行耗时,渲染次数等关键信息。咱们能够有针对性的进行优化。
注意:生产环境不要引入Perf
这是React官网对组件渲染机制的描述图,其中绿色组件表明不须要更新,红色组件须要更新,影响更新的条件主要有SCU(shouldComponentUpdate)及DOM DIff结果。
咱们再来看看 组件触发更新的流程图
:
经过上述流程图,再对比渲染的图解能够看到,React的性能瓶颈主要出如今生成DOM及DOM Diff的过程
。若是进行性能优化,关键在:
针对文章开头提出的两个性能问题,咱们获得如下解决方案:
shouldComponentUpdate
方法,自行决定是否更新咱们能够经过控制子组件的 shouldComponentUpdate
从而控制是否渲染:
// 接收两个参数,分别为待更新的属性及状态值 shouldComponentUpdate(nextProps, nextState) { // 若是当前的value值与待更新不相等,才执行更新 return this.props.value !== nextProps.value; }
针对列表遍历类型,遍历时候增长惟一 key
属性值,对子组件进行惟一性识别,准确知道要操做的子组件,提升 DOM Diff 的效率。
array.map(val, key) => {
return <span key={key}>{val}</span> })
为了提升React组件渲染性能,React 针对组件的 shouldComponentUpdate
方法进行了封装处理,咱们不须要在每一个组件里面手动编写 shouldComponentUpdate
。
React在以前版本提供了 PureRenderMixin
的mixin形式,其用法以下:
// react官方demo import PureRenderMixin from 'react-addons-pure-render-mixin'; class FooComponent extends React.Component { constructor(props) { super(props); this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this); }
其原理就是重写了 shouldComponentUpdate 方法。
React 15.3.0 新增了一个 PureComponent
类,以 ES2015 class 的方式方便地定义纯组件 (pure component),用于取代以前的 PureRenderMixin
。
这个类的用法很简单,若是你有些组件是纯组件,那么把继承类从 Component 换成 PureComponent 便可。当组件更新时,若是组件的 props 和 state 都没发生改变,render 方法就不会触发,省去 Virtual DOM 的生成和比对过程,达到提高性能的目的。
import React, { PureComponent } from 'react' class Example extends PureComponent { render() { // ... } }
这里要注意的是:PureRenderMixin、PureComponent
内进行的仅仅是浅比较对象(shallowCompare
)。若是对象包含了复杂的数据结构,深层次的差别可能会产生误判。好比,若是咱们的state变为:
state = {
value: { foo: 'bar' } } // 每次更改value值的时候进行: this.setState({ value: newValue });
此时直接经过值的比较是行不通的,由于对象的引用关系,致使在子组件里面接受到的 this.props.value
与 nextProps.value
永远都是相等的。这里的解决方案主要有:
虽然React提供了Virtual DOM
DOM Diff
等优秀的能力来提升渲染性能,可是在实际使用过程当中,咱们常常会遇到父组件更新,不须要更新因此子组件的场景(分页),此时必须考虑利用React本周的渲染机制来进行优化。