React生命周期的实现

根据上一下节我们可以知道如果你的组件是继承于 React.Component,那么它会进入 ReactCompositeComponent,这是一个专门处理生命周期的逻辑,你可以阅读 src/renderers/shared/reconciler/ReactCompositeComponent.js 文件,它的起始调用栈是通过一个事务进入了 mountComponentIntoNode 函数,在这个函数中最后进入 mountComponent 方法中。

中间经过了一个非常简单的方法,感觉起来就是一个中转的 mountComponent,可以在 src/renderers/shared/reconciler/ReactReconciler.js 文件中阅读。

mountComponent

mountComponent 中 很大一段都是处理警告信息以及初始化的动作,重点在于:

if (inst.unstable_handleError) {
  markup = this.performInitialMountWithErrorHandling(
    renderedElement,
    nativeParent,
    nativeContainerInfo,
    transaction,
    context
  );
} else {
  markup = this.performInitialMount(renderedElement, nativeParent, nativeContainerInfo, transaction, context);
}

进入了 performInitialMount 方法中,在这个方法内它调用了 componentWillMount 也就是在组件挂载之前的生命周期钩子方法。然后通过 renderedElement = this._renderValidatedComponent(); 来获取 ReactElement 创建的对象,并且通过 this._renderedComponent = this._instantiateReactComponent( renderedElement ); 来获取组件实例,然后继续调用了 ReactReconciler.mountComponent ,其实又回到了 mountComponent 中,如此循环下去,一直到最后,将组件都处理完毕。

 if (inst.componentDidMount) {
   if (__DEV__) {
    transaction.getReactMountReady().enqueue(invokeComponentDidMountWithTimer, this);
   } else {
    transaction.getReactMountReady().enqueue(inst.componentDidMount, inst);
   }
 }

以及componentDidMount 方法是加入到了一个事务中的队列内。通过源码我们可以很清晰的看见一个很有趣的现象,父组件的 componentWillMount 方法一定在子组件的 componentWillMount 方法之前调用。父组件的 componentDidMount 方法一定在子组件的 componentDidMount方法之后调用。当然,如果你在 componentWillMount 中使用了 setState 方法,那么这又会进入另外一个流程,不过你可以放心它肯定不会调用 render 方法,而是去合并 state ,这一个步骤要在 update 中描述。

if (this._pendingStateQueue) {
  inst.state = this._processPendingState(inst.props, inst.context);
}

目前为止,React生命周期的前一部分处理完毕。

updateComponent

update 阶段是对于调用了 setState 方法时的处理,这也意味着你需要更新组件。不过这一个阶段,你需要理解的是当你调用 setState 时并不是意味着进入更新,而是它将 state 加入了一个临时队列(关于 state 的部分,会在后续的更新机制中说明),不过它的调用栈流程,会进入 updateComponent 方法中。

在这个方法中首先处理的是 componentWillReceiveProps 方法,然后才会处理 shouldComponentUpdate 并且将前后两个 props 传递给你,根据你的返回值来决定是否调用 _performComponentUpdate 方法,如果你返回的为true,那么你的组件会进入 componentWillUpdate,最后开始执行 _updateRenderedComponent 来重新渲染组件。

最后当你的组件渲染完成之后,则会进入 componentDidUpdate ,当然前提是这个方法是存在的。_pendingForceUpdate 是非常重要的一个状态,你只需要知道,当这个状态为false时并且 shouldComponentUpdate存在时它才会调用。


//更新组件,__DEV__ 可忽略。

var inst = this._instance;
var willReceive = false;
var nextContext;
var nextProps;

// Determine if the context has changed or not
if (this._context === nextUnmaskedContext) {
  nextContext = inst.context;
} else {
  nextContext = this._processContext(nextUnmaskedContext);
  willReceive = true;
}

// Distinguish between a props update versus a simple state update
if (prevParentElement === nextParentElement) {
  // Skip checking prop types again -- we don't read inst.props to avoid
  // warning for DOM component props in this upgrade
  nextProps = nextParentElement.props;
} else {
  nextProps = this._processProps(nextParentElement.props);
  willReceive = true;
}

// An update here will schedule an update but immediately set
// _pendingStateQueue which will ensure that any state updates gets
// immediately reconciled instead of waiting for the next batch.
if (willReceive && inst.componentWillReceiveProps) {
  if (__DEV__) {
    if (this._debugID !== 0) {
      ReactInstrumentation.debugTool.onBeginLifeCycleTimer(
        this._debugID,
        'componentWillReceiveProps'
      );
    }
  }
  inst.componentWillReceiveProps(nextProps, nextContext);
  if (__DEV__) {
    if (this._debugID !== 0) {
      ReactInstrumentation.debugTool.onEndLifeCycleTimer(
        this._debugID,
        'componentWillReceiveProps'
      );
    }
  }
}

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

if (!this._pendingForceUpdate && inst.shouldComponentUpdate) {
  if (__DEV__) {
    if (this._debugID !== 0) {
      ReactInstrumentation.debugTool.onBeginLifeCycleTimer(
        this._debugID,
        'shouldComponentUpdate'
      );
    }
  }
  shouldUpdate = inst.shouldComponentUpdate(nextProps, nextState, nextContext);
  if (__DEV__) {
    if (this._debugID !== 0) {
      ReactInstrumentation.debugTool.onEndLifeCycleTimer(
        this._debugID,
        'shouldComponentUpdate'
      );
    }
  }
}

if (__DEV__) {
  warning(
    shouldUpdate !== undefined,
    '%s.shouldComponentUpdate(): Returned undefined instead of a ' +
    'boolean value. Make sure to return true or false.',
    this.getName() || 'ReactCompositeComponent'
  );
}

this._updateBatchNumber = null;
if (shouldUpdate) {
  this._pendingForceUpdate = false;
  // Will set `this.props`, `this.state` and `this.context`.
  this._performComponentUpdate(
    nextParentElement,
    nextProps,
    nextState,
    nextContext,
    transaction,
    nextUnmaskedContext
  );
} else {
  // If it's determined that a component should not update, we still want
  // to set props and state but we shortcut the rest of the update.
  this._currentElement = nextParentElement;
  this._context = nextUnmaskedContext;
  inst.props = nextProps;
  inst.state = nextState;
  inst.context = nextContext;
}

.....
// _performComponentUpdate 调用栈中

this._updateRenderedComponent(transaction, unmaskedContext);

//如果存在 componentDidUpdate
transaction.getReactMountReady().enqueue(
   inst.componentDidUpdate.bind(inst, prevProps, prevState, prevContext),
   inst
);

当你进入 _updateRenderedComponent 流程时,于是又开始了 updateComponent 的调用,这里跟 mountComponent 的流程有些类似。父组件的 componentWillUpdate 一定会在子组件的 componentWillUpdate 之前调用,父组件的 componentDidUpdate 一定会在子组件的 componentDidUpdate 之后调用。

unmountComponent

unmountComponent的调用必然是在你调用了 setState 方法之后并且需要更新组件时,因为这了牵扯到了 setState 更新机制,详细的会在后期慢慢描述,在这个生命周期中,你只需要注意一个方法 componentWillUnmount

你可以在源码中看见,很多变量都设置成了null。

results matching ""

    No results matching ""