React源码学习入门(十一)React组件更新流程详解

本文基于React v15.6.2版本介绍,原因请参见新手如何学习React源码

源码分析

上一篇文章提到最后更新组件是走到了performUpdateIfNecessary方法,让我们来看一看它的实现:

  performUpdateIfNecessary: function(transaction) {
    if (this._pendingElement != null) {
      ReactReconciler.receiveComponent(
        this,
        this._pendingElement,
        transaction,
        this._context,
      );
    } else if (this._pendingStateQueue !== null || this._pendingForceUpdate) {
      this.updateComponent(
        transaction,
        this._currentElement,
        this._currentElement,
        this._context,
        this._context,
      );
    } else {
      this._updateBatchNumber = null;
    }
  },

这个方法其实最终走到的是updateComponent方法,并且注意的是,在我们更新state的当前这个组件,它传入的prev和next都是相同的,这个后面会决定willReceiveProps会不会触发。

接下来就是React组件核心更新方法updateComponent,源码位于src/renderers/shared/stack/reconciler/ReactCompositeComponent.js

这个函数核心做了3件事情:

  1. 触发componentWillReceiveProps钩子,这个钩子的触发条件是当context或element发生变化时,显然,刚刚我们进来时发现这里的prev和next都是一样的,也就是触发setState的那个组件是不会调用componentWillReceiveProps的。

  2. 合并当前的未处理的state,这个就是将之前setState插入队列里的state一次性合并到当前的state上,这里的合并用的是Object.assign

  3. 计算shouldUpdate,shouldUpdate默认为true,这也是React最大程度保证了组件都能被更新到,我们可以在组件里面实现自己的shouldComponentUpdate方法来决定是否重新render,另外对于PureComponent来说,这里通过shallowEqual来判断state和props是否发生了变化,主要利用的是Object.is判断是否相等。

当组件被判定为shouldUpdate的时候,就会走到_performComponentUpdate来执行更新:

这个函数主要做了以下几件事:

  1. 触发componentWillUpdate钩子

  2. 更新当前组件实例的props和state

  3. 更新子组件

  4. componentDidUpdate入队,这个和componentDidMount是一样的,都是通过Reconciler的transaction在close阶段按照队列触发。

接下来着重看一下更新子组件的流程:

这个函数核心是判断shouldUpdateReactComponent,如果是的话,那就走子组件的更新流程,否则,就销毁子组件,重新挂载。

一般来说,针对子组件的销毁和重建是比较消耗性能的,而且会使得生命周期函数被重复触发,所以React采用一个简单的原则来判断是否需要重新挂载,这也是Diff算法的起点:

解读一下这个关键函数,分几类情况:

  1. emptyComponent的场景,如果同为false或者同为null,则不需要重新挂载,否则重新挂载。

  2. stringnumber的场景,也就是一个文本节点,前后都是文本节点的话,是不需要重新挂载的。

  3. 其他的情况,得看两个组件是否是同一个类型,以及key是否相同,若两个条件同时满足,则不需要重新挂载。

所有触发的子组件,默认按照receiveComponent的模式往下递归,如果遇到React组件,又会重复之前的步骤,它的入口是:

updateComponent流程上面已经分析过了,不再赘述。

小结一下

本文主要分析了React组件的更新过程,重在几个生命周期函数的触发,以及更新策略,具体真正的更新是在DOMComponent中。我们可以简单总结一下React组件更新的流程图:

Last updated