React 的性能优化 shouldComponentUpdate 与 PureComponent

react 的一个常见优化方式是使用 shouldComponentUpdate 生命周期对不需要更新的组件调停,但当组件多起来就非常麻烦,那有没有更合理方便的方式呢?

React 修改状态

在 React 中,每次 setState , Virtual DOM 会计算出前后两次虚拟 DOM 对象的区别,再去修改真实需要修改的 DOM 。由于 js 计算速度很快,而操作真实 DOM 较慢,Virtual DOM 避免了没必要的真实 DOM 操作,提高了性能。

React 通过地址引用的方式判断对象是否变更,所以我们每次 setState 应新建一个新的对象,而不是在原有对象上操作,否则无法更新组件。

/*
DefaultState = {
color:'red',
count:1
}
*/

this.setState(state) => {
    return {
    ...state,
    count:2
    }
}

使用 shouldComponentUpdate 避免不必要的渲染

React 在 setState 时会不仅会重新渲染该组件自身,依赖于该 state 的子组件也会被更新,无论修改的 state 子组件是否需要。

在组件 setState 后、render之前,会触发 shouldComponentUpdate 函数,我们可以通过调整该函数的返回值来判断组件是否需要更新。

class CounterButton extends React.Component {
  constructor(props) {
    super(props);
    this.state = {count: 1};
  }

  shouldComponentUpdate(nextProps, nextState) {
    if (this.props.color !== nextProps.color) {
      return true;
    }
    if (this.state.count !== nextState.count) {
      return true;
    }
    return false;
  }

  render() {
    return (
      <button
        color={this.props.color}
        // 这里直接对原有 state 修改(应新建对象),如果不使用 shouldComponentUpdate 判断更新,默认组件是不会更新的。
        onClick={() => this.setState(state => ({count: state.count + 1}))}>
        Count: {this.state.count}
      </button>
    );
  }
}

在上面代码中,我们通过对比当前 props,state 与 修改后的 nextProps,nextProps 做对比,在需要更新时返回 true,反之返回 false。

这样组件就只会在 count 或 color 发生变化时更新。

PureComponent

React15.3 中新加了一个类PureComponent,和 Component 基本一样,只不过会在 render 之前帮组件自动执行一次 shallowEqual(浅比较),来决定是否更新组件。浅比较类似于浅复制,只会比较第一层。使用 PureComponent 相当于省去了写 shouldComponentUpdate 函数,当组件更新时,如果组件的 props 和 state:

  1. 引用和第一层数据都没发生改变, render 方法就不会触发,这是我们需要达到的效果。
  2. 虽然第一层数据没变,但引用变了,就会造成虚拟 DOM 计算的浪费。
  3. 第一层数据改变,但引用没变,会造成不渲染,所以需要很小心的操作数据。

Immutable.js

使用 PureComponent 是方便了,但万一忘了或状态复杂导致操作的是原有对象而非新建对象,可能会产生难以寻找的 Bug ,而 Immutable.js 可以避免该问题。

Immutable.js 的作用是使数据一旦创建就不能修改,对数据操作是通过 set 或 merge 等方式新建对象并修改。

import { fromJS, isImmutable } from "immutable";  

let oldObj = {  
  a: 'test',
  b: [1, 2, 4]
};

let newObj = fromJS(oldObj);

newObj.a = 'aaaa' //报错

newObj.set('a','aaa').set('b',[2,3,4]); // 修改完后的 newObj 与之前的 newObj 是两个不同的对象

newObj.merge({
a: fromJS('aaa'),
b: fromJS([2,3,4])
})


const obj1 = newObj.toJS(); // 转换成原生 `js` 类型  

所以通过 ImmutableJS 将状态都变为不可变更, 结合 PureComponent 可以很大程度的减少非必要的组件渲染,可以大量的提高性能。

参考

Immutable 详解及 React 中实践
React 的性能优化(一)当 PureComponent 遇上 ImmutableJS

评论

此博客中的热门博文

1. Angular 错误:ExpressionChangedAfterItHasBeenCheckedError