React性能提高

了解react如何提高性能将有助于咱们更好的编写代码。我的认为react中不少的性能优化,其实都是围绕着react的核心diff算法来展开的,经过优化,减小diff算法中一些没必要要的步骤,从而来提升性能。下面是我平时开发总结出来的一些经验。react

Component与PureComponent

通常来说,不少人都是使用的Component,可是会带来一个问题,那就是在父组件的props或者state发生改变的时候,或者说的再准确一点,每当咱们调用setState或者props发生改变的时候,都会再执行一次render,会从新生成virtualdom,这个时候,好比说在该父组件中还存在HomeArea两个组件,也会从新进行渲染(执行render -> 生成virtualDom -> 对比新旧virtualDom生成差别对象 -> 将生成的差别对象应用到真实dom上),可是若是HomeArea没有任何改变那么差别对象不包含任何东西,那么就至关于从新渲染的时候前三步耗费了性能。算法

这种性能耗费实际上是不必的, 因而react提供了两种方法给咱们来优化这个过程。redux

Component中shouldComponentUpdate方法

该方法在Component中没有继承实现,须要咱们手动实现。而且在初次渲染和调用forceUpdate的时候不触发数组

  • shouldComponentUpdate(nextProps, nextState)

若是咱们要判断该组件是否更新必须经过比较this.propsnextProps加上this.statenextState, 判断他们是否改变,由于默认是返回true的。若是改变了能够经过返回true来告诉react,须要进行前面说到的从新渲染的过程,否者返回false。那么shouldComponentUpdate后面的生命周期render() -> getSnapshotBeforeUpdate() -> componentDidUpdate()都不会执行。这样就达到了提升性能的做用。性能优化

可是须要注意的一点是,虽然该组件没有被re-rendering,可是若是该组件中还包含子组件,若是此时子组件的state(这里我没有说还有props)发生了改变,此子组件仍是会被从新渲染,并非说父组件的shouldComponentUpdate返回false,全部的子组件就不会从新渲染了。你可能如今会有点迷糊,不是说返回false就不会从新渲染也不会执行render的吗? 那怎么会从新渲染子组件呢? 让咱们来想想:若是经过比较发现该组件的stateprops都没有发生改变,说明传给子组件的props确定没有变,此时惟一可能改变的就是子组件自生的state。在开发中可能会出现这种状况,即父组件状态没有改变,可是子组件自身的state发生改变。 并且在实际项目开发中,结合使用redux来管理数据的时候,若是父组件的状态没有发生改变,子组件其实可能仍是会受到props的影响的。数据结构

还有一点是在比较的过程当中,若是过于复杂也会致使过多的性能损耗,还不如不进行比较,进行默认的步骤。例如深度遍历、JSON.stringify这种耗费大量的性能的操做。一般比较推荐的作法是进行浅比较,而且下面要介绍的PureComponent也是使用的浅比较,什么是浅比较在下面PureComponent专题中说。dom

使用PureComponent

PureComponent内部帮咱们实现了shouldComponentUpdate,其余和Component同样。可是在shouldComponentUpdate进行的是一个浅比较,看看官方文档是怎么说的。函数

clipboard.png
他这里并无仔细说浅比交究竟是怎么比较。通过个人调试,我发现浅比较具备如下特色。性能

  1. 对于state只比较第一层,而且若是是基本类型,那么比较他们的值,若是是引用值,那么比较他们的引用。
  2. 对于传递来的props, 也只比较第一层,规则和上面同样。

下面给几个例子:假设state中的数据结构是这样优化

constructor (props) {
    super(props)
    this.state = {
      count: 0,
      person: {
        // name: 'longjincen'
        person: {
          person: {
            name: 'longjincen'
          }
        }
      }
    }
  }

当咱们继承的是Component的时候,只要调用setState就会触发从新渲染,可是当咱们继承的是PureComponent的时候,就不同了。当咱们像下面这样改变state的时候

handleClick = () => {
    const { person } = this.state
    person.person.person = {
      name: 'xiaoya'
    }
    this.setState({
      person: person
    })
  }

这个时候最外层的person始终都是同一个引用,因此这个时候是不会触发从新渲染的,就算咱们内部的person是一个新的引用,由于它只比较第一层。因此当咱们想要更新的时候,第一层返回的必须是一个新的对象,才会触发从新渲染。因此官方文档中也说了,若是数据结构比较复杂,那么可能会致使一些问题,要么当你知道改变的时候调用forceUpdate,要么使用immutable来包装你的state,immutable是一个js库,用法和js差很少,是facebook开发出来的,通常和PureComponent搭配使用。

因此当咱们使用PureComponent的时候返回的state必须是一个新的对象,下面给出几种解决办法:

  1. 使用Object.assgin({}, xxx), 来建立一个新的对象返回
  2. 使用扩展运算符{ ...xxx }
1.
  handleClick = () => {
    const { person } = this.state
    person.person.person.name = 'xiaoya'
    this.setState({
      person: Object.assign({}, person)
    })
  }
2.
  handleClick = () => {
    const { person } = this.state
    person.person.person.name = 'xiaoya'
    this.setState({
      person: { ...person }
    })
  }

而对于接收到的先后的props的比较和上面介绍的state比较同样,可是须要注意的一种状况是,若是咱们将上面的第三层的person对象传给子组件,那么在子组件中,因为接收到的person对象先后引用发生了改变,因此尽管父组件不会触发从新渲染,子组件仍是会触发,而若是咱们传递的是第二层或者第一层的person因为引用没有改变,子组件也不会触发从新渲染。

最后要说的是在实际项目开发中每每是使用PureComponentimmutable搭配的方式

其余

无状态组件

无状态组件就是使用定义函数的方式来定义组件,这种组件相比于使用类的方式来定义的组件(有状态组件),少了不少初始化过程,更加精简,因此要是可使用无状态组件应当尽量的使用无状态组件,会大幅度提高效率

不要在render中生成新的引用

定义函数、使用内联样式或者动态生成一些不依赖statepropsjsx这些,凡是不依赖stateprops的都应该提到render以外,不然会形成每次render的时候生成新的引用,会致使在diff算法对比属性或节点的过程当中发现两个引用不一致,对于react来讲,这说明属性值发生了改变,最后会被替换成新的引用,形成性能浪费。例如函数应该放在render函数外定义,样式应该单独建一个文件,而后引入,使用生命周期函数或者自定义函数动态生成一些不依赖statepropsjsx

始终由用户触发改变state

render函数中,只能经过特定的用户事件来触发state改变,否者容易形成不断刷新的死循环,像下面这样就是错误的

render () {
    this.setState({
        name: 'xcacsa'
    })
}

始终使用key值

避免使用数组下标做为key值, 应该确保惟一。在diff算法中,key值用来保证当列表中节点顺序改变的时候节点的复用,而不是所有替换。

相关文章
相关标签/搜索