We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
React是一个专注于UI层的框架,它使用虚拟DOM技术,以保证它UI的高速渲染;使用单向数据流,因此它数据绑定更加简单;那么它内部是如何保持简单高效的UI渲染呢?这种渲染机制有可能存在什么性能问题呢?
React不直接操作DOM,它在内存中维护一个快速响应的DOM描述,render方法返回一个DOM的描述,React能够计算出两个DOM描述的差异,然后更新浏览器中的DOM。这就是著名的DOM Diff。
DOM Diff
就是说React在接收到属性(props)或者状态(state)更新时,就会通过前面的方式更新UI。所以React整个UI渲染是比较快的。但是这里面可能出现的问题是:
假设我们定义一个父组件,其包含了5000个子组件。我们有一个输入框输入操作,每次输入一个数字,对应的那个子组件背景色变红。
<Components> <Components-1 /> <Components-2 /> <Components-3 /> ... <Components-5000 /> </Components>
这样我们输入数字1,则子组件1背景色变化,但是在这个过程中,所有的子组件都进行了重新渲染,导致整体渲染变慢。造成这种现象的原因是 React中父组件更新默认触发所有子组件更新。(具体例子见文章后demo链接)
React中父组件更新默认触发所有子组件更新
同时,我们经常在遍历列表元素时候会遇到这样的提示:
Warning: Each child in an array or iterator should have a unique "key" prop.
这就是我们今天要讨论的两个性能优化点:
我们利用 react-addons-perf 进行性能检测。引入方法如下:
react-addons-perf
import Perf from 'react-addons-perf' window.Perf = Perf // 挂载到全局变量方便使用
检测方法,在浏览器控制台输入如下命令:
控制台会以表格的形式展示出结果:
上图记录了每个组件的执行耗时,渲染次数等关键信息。我们可以有针对性的进行优化。
注意:生产环境不要引入Perf
这是React官网对组件渲染机制的描述图,其中绿色组件代表不需要更新,红色组件需要更新,影响更新的条件主要有SCU(shouldComponentUpdate)及DOM DIff结果。
我们再来看看 组件触发更新的流程图:
组件触发更新的流程图
通过上述流程图,再对比渲染的图解可以看到,React的性能瓶颈主要出现在生成DOM及DOM Diff的过程。如果进行性能优化,关键在:
React的性能瓶颈主要出现在生成DOM及DOM Diff的过程
针对文章开头提出的两个性能问题,我们得到以下解决方案:
shouldComponentUpdate
我们可以通过控制子组件的 shouldComponentUpdate 从而控制是否渲染:
// 接收两个参数,分别为待更新的属性及状态值 shouldComponentUpdate(nextProps, nextState) { // 如果当前的value值与待更新不相等,才执行更新 return this.props.value !== nextProps.value; }
针对列表遍历类型,遍历时候增加唯一 key 属性值,对子组件进行唯一性识别,准确知道要操作的子组件,提高 DOM Diff 的效率。
key
array.map(val, key) => { return <span key={key}>{val}</span> })
为了提高React组件渲染性能,React 针对组件的 shouldComponentUpdate 方法进行了封装处理,我们不需要在每个组件里面手动编写 shouldComponentUpdate。
React在之前版本提供了 PureRenderMixin 的mixin形式,其用法如下:
PureRenderMixin
// 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。
PureComponent
这个类的用法很简单,如果你有些组件是纯组件,那么把继承类从 Component 换成 PureComponent 即可。当组件更新时,如果组件的 props 和 state 都没发生改变,render 方法就不会触发,省去 Virtual DOM 的生成和比对过程,达到提升性能的目的。
import React, { PureComponent } from 'react' class Example extends PureComponent { render() { // ... } }
这里要注意的是:PureRenderMixin、PureComponent 内进行的仅仅是浅比较对象(shallowCompare)。如果对象包含了复杂的数据结构,深层次的差异可能会产生误判。比如,如果我们的state变为:
PureRenderMixin、PureComponent
shallowCompare
state = { value: { foo: 'bar' } } // 每次更改value值的时候进行: this.setState({ value: newValue });
此时直接通过值的比较是行不通的,因为对象的引用关系,导致在子组件里面接受到的 this.props.value 与 nextProps.value 永远都是相等的。这里的解决方案主要有:
this.props.value
nextProps.value
虽然React提供了Virtual DOM DOM Diff 等优秀的能力来提高渲染性能,但是在实际使用过程中,我们经常会遇到父组件更新,不需要更新所以子组件的场景(分页),此时必须考虑利用React本周的渲染机制来进行优化。
Virtual DOM
附: 文章涉及的相关代码
The text was updated successfully, but these errors were encountered:
总结的非常好。
顺便分享一个个人比较喜欢的 immutable 实现方案 dot-prop-immutable,轻量级,API 比较人性化,这是修了些 issue 的 fork.
还有一个偶然发现的一个坑,如果在 render 中使用过箭头函数来传参, () => this.handleClick('Some param'),那么什么 immutable 方案就都不起作用了,因为每次都会生成一个全新的函数对象。。解决办法是使用 memoize,比如 memoize-bind 或者用装饰器在方法上加 lodash 的 _.memoize
() => this.handleClick('Some param')
_.memoize
Sorry, something went wrong.
@zaaack 直接使用箭头函数的问题,因为你是直接执行了啊,不用memoize直接用this.handleClick.bind(this,'someparam')这样不可以吗?
@younth 这是个比较经典的问题,我也是在别的地方上看来的,如果要传参,memoize是惟一的办法,因为bind也会生成新的函数,所以bind效率低,所以有我发的memoize-bind。。
No branches or pull requests
React是一个专注于UI层的框架,它使用虚拟DOM技术,以保证它UI的高速渲染;使用单向数据流,因此它数据绑定更加简单;那么它内部是如何保持简单高效的UI渲染呢?这种渲染机制有可能存在什么性能问题呢?
React组件渲染问题引出
React不直接操作DOM,它在内存中维护一个快速响应的DOM描述,render方法返回一个DOM的描述,React能够计算出两个DOM描述的差异,然后更新浏览器中的DOM。这就是著名的
DOM Diff
。就是说React在接收到属性(props)或者状态(state)更新时,就会通过前面的方式更新UI。所以React整个UI渲染是比较快的。但是这里面可能出现的问题是:
假设我们定义一个父组件,其包含了5000个子组件。我们有一个输入框输入操作,每次输入一个数字,对应的那个子组件背景色变红。
这样我们输入数字1,则子组件1背景色变化,但是在这个过程中,所有的子组件都进行了重新渲染,导致整体渲染变慢。造成这种现象的原因是
React中父组件更新默认触发所有子组件更新
。(具体例子见文章后demo链接)同时,我们经常在遍历列表元素时候会遇到这样的提示:
这就是我们今天要讨论的两个性能优化点:
React性能检测工具
我们利用
react-addons-perf
进行性能检测。引入方法如下:检测方法,在浏览器控制台输入如下命令:
控制台会以表格的形式展示出结果:
上图记录了每个组件的执行耗时,渲染次数等关键信息。我们可以有针对性的进行优化。
注意:生产环境不要引入Perf
React 性能优化原理
这是React官网对组件渲染机制的描述图,其中绿色组件代表不需要更新,红色组件需要更新,影响更新的条件主要有SCU(shouldComponentUpdate)及DOM DIff结果。
我们再来看看
组件触发更新的流程图
:通过上述流程图,再对比渲染的图解可以看到,
React的性能瓶颈主要出现在生成DOM及DOM Diff的过程
。如果进行性能优化,关键在:如何提高组件的渲染效率
针对文章开头提出的两个性能问题,我们得到以下解决方案:
shouldComponentUpdate
方法,自行决定是否更新我们可以通过控制子组件的
shouldComponentUpdate
从而控制是否渲染:针对列表遍历类型,遍历时候增加唯一
key
属性值,对子组件进行唯一性识别,准确知道要操作的子组件,提高 DOM Diff 的效率。PureRenderMixin 与 PureComponent
为了提高React组件渲染性能,React 针对组件的
shouldComponentUpdate
方法进行了封装处理,我们不需要在每个组件里面手动编写shouldComponentUpdate
。PureRenderMixin
React在之前版本提供了
PureRenderMixin
的mixin形式,其用法如下:其原理就是重写了 shouldComponentUpdate 方法。
PureComponent
React 15.3.0 新增了一个
PureComponent
类,以 ES2015 class 的方式方便地定义纯组件 (pure component),用于取代之前的PureRenderMixin
。这个类的用法很简单,如果你有些组件是纯组件,那么把继承类从 Component 换成 PureComponent 即可。当组件更新时,如果组件的 props 和 state 都没发生改变,render 方法就不会触发,省去 Virtual DOM 的生成和比对过程,达到提升性能的目的。
这里要注意的是:
PureRenderMixin、PureComponent
内进行的仅仅是浅比较对象(shallowCompare
)。如果对象包含了复杂的数据结构,深层次的差异可能会产生误判。比如,如果我们的state变为:此时直接通过值的比较是行不通的,因为对象的引用关系,导致在子组件里面接受到的
this.props.value
与nextProps.value
永远都是相等的。这里的解决方案主要有:总结
虽然React提供了
Virtual DOM
DOM Diff
等优秀的能力来提高渲染性能,但是在实际使用过程中,我们经常会遇到父组件更新,不需要更新所以子组件的场景(分页),此时必须考虑利用React本周的渲染机制来进行优化。附: 文章涉及的相关代码
The text was updated successfully, but these errors were encountered: