react benchmark

简单的说一下如何在 react 开发中,进行 benchmark,而且会简单的说一些能够提高性能的小技巧。html

Performance

既然想要进行 benchmark,那么就须要一个指标来衡量。react 官方已经提供了这样的工具供开发人员使用。react

官方的文档能够参考这里Performance Tools,在这里我再作个简单的介绍。git

引入

import Perf from 'react-addons-perf';

就能够将Performance Tool引入到当前的 react app 中使用了。github

使用

Performance Tool的使用十分方便,在上一步引入的 Perf 自己是个对象,能够直接调用其方法,来进行性能测试。app

获取指标的方法
  • Perf.start(): 开始记录
  • Perf.stop(): 中止记录
  • Perf.getLastMeasurements(): 获取最后一条记录
输出指标的方法

如下这些方法,若是不传入参数的话,都会调用Perf.getLastMeasurements()方法,获取最新的 measurementsdom

  • Perf.printInclusive(measurements): 输出组件 lifecirlifecyclecle 所有执行完的时间
  • Perf.printExclusive(measurements): 只输出组件渲染所用的时间,不包括 componentWillMountcomponentDidMount的执行时间
  • Perf.printWasted(measurements): Wasted的是指页面中的 dom 实际并无发生变化,可是组件仍然被渲染的操做,printWasted就是输出这些无心义操做所用的时间
  • Perf.printOperations(measurements): 详细输出每个组件dom 操做(包括 innerHTML 和 remove)所用的时间和相关信息
一个实例截图

一个截图

一些优化技巧

functional

举个简单的例子,假设咱们须要渲染一个并无交互的组件,例如一句话,那么这个组件其实也不存在 lifecycle,那么能够直接使用函数式的方法输出这个组件函数

我作了个简单的 demo,能够 clone 下来本身看下工具

// 使用 component
class Text extends React.Component {
    render () {
        return (
            <div>{ this.props.text }</div>
        )
    }
}
export default Text


// 使用匿名函数
export default (text) => {
    return (
        <div>{ text }</div>
    )
}

看下 Performance Tool输出的结果性能

一个截图

能够明显的看到Benchmark > FunctionWrap的总时间要小于Benchmark > ComponentWrapBenchmark > PureComponentWrap所用的时间测试

这是由于使用匿名函数,省掉了 lifecycle 的一系列函数调用的时间,Benchmark > PureCompnent耗时最长是由于React.PureComponent会在shouldComponentUpdate中默认进行shallowEqual的操做,因此初始化渲染会比较慢。

使用 PureComponent

React.PureComponentReact.Component的区别就在于PureComponent会默认带一个shouldComponentUpdate的方法,经过shallowEqual对比当前的 component 是否须要进行从新渲染。

有了这样的一个简单的判断,在不手动写shouldComponentUpdate方法时,也能够得到必定的性能提高。

具体的测试,一样能够看这个demo,里面有相关的测试。

一个隐形的坑

一般在可交互的组件上,咱们会绑定一些事件,例以下面的例子

class User extends React.Component {
  render () {
    console.log('render user')
    return (
      <div className='user'>
        <p>is a component</p>
        <p>name: { this.props.data.name }</p>
        <p>id: { this.props.data.id }</p>
        <div className='buttons'>
          <button onClick={ this.props.onClick }>Change Data</button>
        </div>
      </div>
    )
  }
}

class PureUser extends React.PureComponent {

  render () {
    console.log('render pure user')
    return (
      <div className='user'>
        <p>is a pure component</p>
        <p>name: { this.props.data.name }</p>
        <p>id: { this.props.data.id }</p>
        <div className='buttons'>
          <button onClick={ this.props.onClick }>Change Data</button>
        </div>
      </div>
    )
  }
}

class Anonymous extends React.Component {

  constructor (props) {
    super(props)
    this.state = {
      data: Foo
    }
  }

  render () {

    return (
      <div>
        <div className='wrap'>
          { this.renderUser() }
          { this.renderPureUser() }
        </div>        
      </div>
    )
  }

  changeData () {
    Pref.start()
    this.setState({
      data: Foo
    })
  }

  renderUser () {
    return <User data={ this.state.data } 
            onClick={ this.changeData.bind(this) } />
  }
  
  renderPureUser () {
    return <PureUser data={ this.state.data } 
            onClick={ this.changeData.bind(this) } />
  }
}

在这个例子中<Anonymous />会将state.data传递给自组件,同时会传递一个 onClick 的事件回调给子组件。

在运行这个例子时,咱们会发现即便咱们使用React.PureComponent,而且并无实际改变 state.data的值,可是 <PureUser />这个组件仍是会跟<User /> 组件同样,会重复被渲染。

究其缘由,在于onClick这里使用了.bind方法,将changeData绑定到了当前的做用域内,可是.bind方法返回的是个匿名函数,因此事实上每次传入到子组件内的props都是不一样的,PureComponent也会被从新渲染。

为了不这种状况,能够将.bind方法前置,改在constructor中预先绑定,这样onClick将指向一个固定的函数,例子:

class PublicClassFields extends React.Component {
  constructor (props) {
    super(props)
    this.state = {
      data: Foo
    }
    this.changeData = this.changeData.bind(this)
  }
  ...
  ...
}

这样的话,PureUser在执行 changeData后就不会被从新渲染了。

ps

后续还会有一些关于 react 性能相关的内容补充进来,同时也会不断的更新这个 repo中的实例。

相关文章
相关标签/搜索