简单的说一下如何在 react 开发中,进行 benchmark,并且会简单的说一些可以提升性能的小技巧。
既然想要进行 benchmark,那么就需要一个指标来衡量。react 官方已经提供了这样的工具供开发人员使用。
官方的文档可以参考这里Performance Tools,在这里我再做个简单的介绍。
import Perf from 'react-addons-perf';
就可以将Performance Tool
引入到当前的 react app 中使用了。
Performance Tool
的使用十分方便,在上一步引入的 Perf
本身是个对象,可以直接调用其方法,来进行性能测试。
以下这些方法,如果不传入参数的话,都会调用Perf.getLastMeasurements()
方法,获取最新的 measurements
componentWillMount
和componentDidMount
的执行时间Wasted
的是指页面中的 dom 实际并没有发生变化,但是组件仍然被渲染的操作,printWasted
就是输出这些无意义
操作所用的时间举个简单的例子,假设我们需要渲染一个并没有交互的组件,例如一句话,那么这个组件其实也不存在 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 > ComponentWrap
和Benchmark > PureComponentWrap
所用的时间
这是因为使用匿名函数,省掉了 lifecycle
的一系列函数调用的时间,Benchmark > PureCompnent
耗时最长是因为React.PureComponent
会在shouldComponentUpdate
中默认进行shallowEqual
的操作,所以初始化渲染会比较慢。
React.PureComponent
和React.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
后就不会被重新渲染了。
后续还会有一些关于 react 性能相关的内容补充进来,同时也会不断的更新这个 repo中的实例。