SunShinewyf/issue-blog

React 更新组件的源码解读

SunShinewyf opened this issue · 0 comments

React 在触发更新的时候执行的主要源码

精简主要代码如下:

  updateComponent: function(
    transaction,
    prevParentElement,
    nextParentElement,
    prevUnmaskedContext,
    nextUnmaskedContext
  ) {
    var inst = this._instance;

    var willReceive = false;
    var nextContext;

    //省略

    var prevProps = prevParentElement.props;
    var nextProps = nextParentElement.props;

    //如果是 props 发生更新,而不是 state 的简单更新
    if (prevParentElement !== nextParentElement) {
      willReceive = true;
    }

    //如果是 props 更新,则触发 componentWillReceiveProps()
    if (willReceive && inst.componentWillReceiveProps) {
      if (__DEV__) {
        measureLifeCyclePerf(
          () => inst.componentWillReceiveProps(nextProps, nextContext),
          this._debugID,
          'componentWillReceiveProps',
        );
      } else {
        inst.componentWillReceiveProps(nextProps, nextContext);
      }
    }

    var nextState = this._processPendingState(nextProps, nextContext);
    var shouldUpdate = true;

    if (!this._pendingForceUpdate) {
      if (inst.shouldComponentUpdate) {
          //如果组件实例有 shouldComponentUpdate 则调用
          shouldUpdate = inst.shouldComponentUpdate(nextProps, nextState, nextContext);
    
      } else {
        //如果使用了 PureComponent
        if (this._compositeType === CompositeTypes.PureClass) {
          shouldUpdate =
            !shallowEqual(prevProps, nextProps) ||
            !shallowEqual(inst.state, nextState);
        }
      }
    }

    this._updateBatchNumber = null;
    if (shouldUpdate) {
      //执行更新操作
      this._performComponentUpdate(
        nextParentElement,
        nextProps,
        nextState,
        nextContext,
        transaction,
        nextUnmaskedContext
      );
    } else {
     
      this._currentElement = nextParentElement;
      this._context = nextUnmaskedContext;
      inst.props = nextProps;
      inst.state = nextState;
      inst.context = nextContext;
    }
  },

在这个函数里,主要是判断是 state 还是 props 引起的变化,如果是 props,则执行实例的 componentWillReceiveProps() ,然后再判断实例是否提供了 shouldComponentUpdate 方法,如果提供,则直接调用,否则再判断是否使用了 PureComponent, 如果使用了,则使用浅比较来判断是否更新。 当前面两者都不满足,则执行 performComponentUpdate。

继续追踪代码,会发现 performComponentUpdate 里面调用了 _updateRenderdComponent ,源码如下:

  _performComponentUpdate: function(
    nextElement,
    nextProps,
    nextState,
    nextContext,
    transaction,
    unmaskedContext
  ) {
    var inst = this._instance;

    var hasComponentDidUpdate = Boolean(inst.componentDidUpdate);
    var prevProps;
    var prevState;
    var prevContext;
    if (hasComponentDidUpdate) {
      prevProps = inst.props;
      prevState = inst.state;
      prevContext = inst.context;
    }

    if (inst.componentWillUpdate) {
        inst.componentWillUpdate(nextProps, nextState, nextContext);      }
    }

    this._currentElement = nextElement;
    this._context = unmaskedContext;
    inst.props = nextProps;
    inst.state = nextState;
    inst.context = nextContext;

    if (inst.unstable_handleError) {
      this._updateRenderedComponentWithErrorHandling(transaction, unmaskedContext);
    } else {
      this._updateRenderedComponent(transaction, unmaskedContext);
    }

    if (hasComponentDidUpdate) {
      if (__DEV__) {
        transaction.getReactMountReady().enqueue(() => {
          measureLifeCyclePerf(
            inst.componentDidUpdate.bind(inst, prevProps, prevState, prevContext),
            this._debugID,
            'componentDidUpdate'
          );
        });
      } else {
        transaction.getReactMountReady().enqueue(
          inst.componentDidUpdate.bind(inst, prevProps, prevState, prevContext),
          inst
        );
      }
    }
  },

_updateRenderedComponent 里又调用了 _updateRenderedComponentWithNextElement ,_updateRenderedComponentWithNextElement 里面 又通过 shouldUpdateReactComponent
来判断组件是否需要更新:

  _updateRenderedComponentWithNextElement: function(
    transaction,
    context,
    nextRenderedElement,
    safely
  ) {
    var prevComponentInstance = this._renderedComponent;
    var prevRenderedElement = prevComponentInstance._currentElement;

    if (shouldUpdateReactComponent(prevRenderedElement, nextRenderedElement)) {
      ReactReconciler.receiveComponent(
        prevComponentInstance,
        nextRenderedElement,
        transaction,
        this._processChildContext(context)
      );
    } else {
      var oldHostNode = ReactReconciler.getHostNode(prevComponentInstance);
      ReactReconciler.unmountComponent(
        prevComponentInstance,
        safely,
        false /* skipLifecycle */
      );

      var nodeType = ReactNodeTypes.getType(nextRenderedElement);
      this._renderedNodeType = nodeType;
      var child = this._instantiateReactComponent(
        nextRenderedElement,
        nodeType !== ReactNodeTypes.EMPTY /* shouldHaveDebugID */
      );
      this._renderedComponent = child;

      var nextMarkup = ReactReconciler.mountComponent(
        child,
        transaction,
        this._hostParent,
        this._hostContainerInfo,
        this._processChildContext(context),
        debugID
      );

      this._replaceNodeWithMarkup(
        oldHostNode,
        nextMarkup,
        prevComponentInstance
      );
    }
  },

shouldUpdateReactComponent 的源码
如下:

function shouldUpdateReactComponent(prevElement, nextElement) {
  var prevEmpty = prevElement === null || prevElement === false;
  var nextEmpty = nextElement === null || nextElement === false;
  if (prevEmpty || nextEmpty) {
    return prevEmpty === nextEmpty;
  }

  var prevType = typeof prevElement;
  var nextType = typeof nextElement;
  if (prevType === 'string' || prevType === 'number') {
    return (nextType === 'string' || nextType === 'number');
  } else {
    return (
      nextType === 'object' &&
      prevElement.type === nextElement.type &&
      prevElement.key === nextElement.key
    );
  }
}

由此发现 React.Component 在这里使用了深比较, 然后之后的更新流程就是使用 diff 算法来比较两次的 Virtual DOM 是否改变从而执行更新操作。