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中的實例。

相關文章
相關標籤/搜索