React性能优化初探

不要将当前container没有用到的state绑定到props

  1. // src/example1/containers/CounterContainer.js
  2. export default connect(state => ({
  3. total: state.example1.total,
  4. }), { increment })(CounterContainer);
  5. // src/example1/containers/RandomNumContainer.js
  6. export default connect(state => ({
  7. randomNum: state.example1.randomNum,
  8. }), { setRandomNum })(RandomNumContainer);

当组件CounterContainer中total改变时,不会引起RandomNumContainer组件render

example1-pure

如果在RandomNumCountainer中加入没有用到的total变量时

  1. // src/example1/containers/RandomNumContainer.js
  2. export default connect(state => ({
  3. randomNum: state.example1.randomNum,
  4. total: state.example1.total,
  5. }), { setRandomNum })(RandomNumContainer);

组件CounterContainer中total改变时,会引起RandomNumContainer组件render

example1-random-counter

需要多次使用state计算得到的数据,请使用reselect缓存

  1. // src/example2/containers/CounterContainer.js
  2. // 主要函数
  3. ...
  4. get formatTotal() {
  5. const { total } = this.props;
  6. console.log('call formatTotal()');
  7. // 复杂的计算
  8. return total * 10;
  9. }
  10. render() {
  11. const { total, clickTime } = this.props;
  12. return (
  13. <div>
  14. <span>CounterContainer: { total }</span>
  15. <button onClick={this.handleClick}>+</button>
  16. <div>格式化后的total: { this.formatTotal }</div>
  17. <div>格式化后的total2: { this.formatTotal }</div>
  18. <button onClick={this.handleTimeClick}>获取点击时间: { clickTime }</button>
  19. </div>
  20. );
  21. }
  22. ...
  23. 1. 点击+号后,会调用两次formatTotal
  24. call formatTotal()
  25. call formatTotal()
  26. 2. 点击获取点击时间,会调用两次formatTotal
  27. call formatTotal()
  28. call formatTotal()
  29. 如果,formatTotal是一个复杂计算的函数,性能是堪忧的。

使用selector优化后的版本

  1. // 将formatTotal传递到组件的props中
  2. const getTotal = state => state.example2.total;
  3. const formatTotal = createSelector(
  4. [getTotal],
  5. (total) => {
  6. console.log('call formatTotal()');
  7. return total + 10;
  8. },
  9. );
  10. export default connect(state => ({
  11. total: state.example2.total,
  12. clickTime: state.example2.clickTime,
  13. formatTotal: formatTotal(state),
  14. }), { increment, setClickTime })(CounterOptimizeContainer);

注意:

  1. 不要将当前container没有用到的state绑定到props
  2. 需要多次使用state计算得到的数据,请使用reselect缓存
  3. 使用PureComponent替代Component,避免不必要的render
  4. 尽量将大组件进行拆分,避免兄弟组件的state改变,引起自身重复render
  5. 避免不必要的props更新。
    1. 避免在render函数中使用this.handleClick.bind(this)以及(id) => this.handleClick(id),如需绑定this,请在constructor中绑定this.handleClick = this.handleClick.bind(this), 如需绑定特定属性值,可以将该组件封装成子组件<ChildComponent data={item} onClick={this.handleClick} />,在子组件中处理回调。
    2. 请慎用{…this.props},尽量只传递子组件需要的props
  6. 子组件使用shouldComponentUpdate时,如果父组件直接修改state值,会因为引用赋值的缘故,继而导致子组件中props值一起改变,这时,即使父组件调用setState更新了子组件的props, 子组件中shouldComponentUpdate也会判断失败,导致子组件更新失败。

Q:

  1. store中为什么要使用immutable数据