React 源码学习(七):生命周期

阅读源码成了今年的学习目标之一,在选择 Vue 和 React 之间,我想先阅读 React 。 在考虑到读哪一个版本的时候,我想先接触到源码早期的思想可能会更轻松一些,最终我选择阅读 0.3-stable 。 那么接下来,我将从几个方面来解读这个版本的源码。javascript

  1. React 源码学习(一):HTML 元素渲染
  2. React 源码学习(二):HTML 子元素渲染
  3. React 源码学习(三):CSS 样式及 DOM 属性
  4. React 源码学习(四):事务机制
  5. React 源码学习(五):事件机制
  6. React 源码学习(六):组件渲染
  7. React 源码学习(七):生命周期
  8. React 源码学习(八):组件更新

那么关于生命周期, React 当中生命周期有 2 个。java

一个是组件的生命周期 _lifeCycleState ,另外一个是复合生命周期 _compositeLifeCycleState 用于复合组件。node

组件生命周期

那么关于组件的生命周期:函数

// core/ReactComponent.js
/** * Every React component is in one of these life cycles. */
var ComponentLifeCycle = keyMirror({
  /** * Mounted components have a DOM node representation and are capable of * receiving new props. */
  // 已挂载
  MOUNTED: null,
  /** * Unmounted components are inactive and cannot receive new props. */
  // 未挂载
  UNMOUNTED: null
});
复制代码

那么咱们来观测到, ReactComponentReactCompositeComponent 关于 ComponentLifeCycle 的状态变化:学习

// core/ReactComponent.js
var ReactComponent = {
  Mixin: {
    getDOMNode: function() {
      // 获取 DOM 节点时,组件必须为已挂载
      invariant(
        this._lifeCycleState === ComponentLifeCycle.MOUNTED,
        'getDOMNode(): A component must be mounted to have a DOM node.'
      );
    },
    construct: function(initialProps, children) {
      // All components start unmounted.
      // 实例化时,组件为未挂载
      this._lifeCycleState = ComponentLifeCycle.UNMOUNTED;
    },
    mountComponent: function(rootID, transaction) {
      // 挂载组件前检查组件应为未挂载
      invariant(
        this._lifeCycleState === ComponentLifeCycle.UNMOUNTED,
        'mountComponent(%s, ...): Can only mount an unmounted component.',
        rootID
      );
      // 挂载完后更新组件生命周期状态
      this._lifeCycleState = ComponentLifeCycle.MOUNTED;
      // Effectively: return '';
    },
    unmountComponent: function() {
      // 卸载前检查组件应为已挂载
      invariant(
        this._lifeCycleState === ComponentLifeCycle.MOUNTED,
        'unmountComponent(): Can only unmount a mounted component.'
      );
      // 卸载完后更新组件生命周期状态
      this._lifeCycleState = ComponentLifeCycle.UNMOUNTED;
    },
    receiveProps: function(nextProps, transaction) {
      // 更新 props 时,组件生命周期应为已挂载
      invariant(
        this._lifeCycleState === ComponentLifeCycle.MOUNTED,
        'receiveProps(...): Can only update a mounted component.'
      );
    },
  }
};
复制代码
// core/ReactCompositeComponent.js
var ReactCompositeComponentMixin = {
  mountComponent: function(rootID, transaction) {
    ReactComponent.Mixin.mountComponent.call(this, rootID, transaction);

    // Unset `this._lifeCycleState` until after this method is finished.
    this._lifeCycleState = ReactComponent.LifeCycle.UNMOUNTED;
    // ...
    this._lifeCycleState = ReactComponent.LifeCycle.MOUNTED;
  },
  replaceState: function(completeState) {
    var compositeLifeCycleState = this._compositeLifeCycleState;
    // 更新 state 时,组件生命周期必须为已挂载,或者复合组件生命周期为挂载中
    invariant(
      this._lifeCycleState === ReactComponent.LifeCycle.MOUNTED ||
      compositeLifeCycleState === CompositeLifeCycle.MOUNTING,
      'replaceState(...): Can only update a mounted (or mounting) component.'
    );
  },
  _bindAutoBindMethod: function(method) {
    function autoBound(a, b, c, d, e, tooMany) {
      // 使用绑定上下文的方法时,组件生命周期必须为已挂载
      if (component._lifeCycleState === ReactComponent.LifeCycle.MOUNTED) {
        return method.call(component, a, b, c, d, e);
      }
    }
  }
};
复制代码

复合组件生命周期

那么接下来,咱们来看看复合生命周期以及其状态变化:this

// core/ReactCompositeComponent.js
/** * `ReactCompositeComponent` maintains an auxiliary life cycle state in * `this._compositeLifeCycleState` (which can be null). * * This is different from the life cycle state maintained by `ReactComponent` in * `this._lifeCycleState`. */
var CompositeLifeCycle = keyMirror({
  /** * Components in the process of being mounted respond to state changes * differently. */
  // 挂载中
  MOUNTING: null,
  /** * Components in the process of being unmounted are guarded against state * changes. */
  // 卸载中
  UNMOUNTING: null,
  /** * Components that are mounted and receiving new props respond to state * changes differently. */
  // 更新 props
  RECEIVING_PROPS: null,
  /** * Components that are mounted and receiving new state are guarded against * additional state changes. */
  // 更新 state
  RECEIVING_STATE: null
});

var ReactCompositeComponentMixin = {
  construct: function(initialProps, children) {
    // 实例化时置空复合生命周期
    this._compositeLifeCycleState = null;
  },
  mountComponent: function(rootID, transaction) {
    // 挂载前设置复合生命周期为挂载中
    this._compositeLifeCycleState = CompositeLifeCycle.MOUNTING;
    // 挂载完成后置空复合生命周期
    // Done with mounting, `setState` will now trigger UI changes.
    this._compositeLifeCycleState = null;
  },
  unmountComponent: function() {
    // 卸载开始时设置复合生命周期为卸载中
    this._compositeLifeCycleState = CompositeLifeCycle.UNMOUNTING;
    if (this.componentWillUnmount) {
      this.componentWillUnmount();
    }
    // 通过生命周期函数 componentWillUnmount 后,置空复合生命周期
    this._compositeLifeCycleState = null;
  },
  receiveProps: function(nextProps, transaction) {
    // 更新 props 时设置复合生命周期为更新 props
    this._compositeLifeCycleState = CompositeLifeCycle.RECEIVING_PROPS;
    if (this.componentWillReceiveProps) {
      this.componentWillReceiveProps(nextProps, transaction);
    }
    // 执行生命周期函数 componentWillReceiveProps 后,设置复合生命周期为更新 state
    this._compositeLifeCycleState = CompositeLifeCycle.RECEIVING_STATE;
    // ...
    // 操做 state 更新相关后,置空复合生命周期
    this._compositeLifeCycleState = null;
  },
  replaceState: function(completeState) {
    // 更新 state
    var compositeLifeCycleState = this._compositeLifeCycleState;
    // 仅限生命周期为挂载中或者复合生命周期为挂载中能够更新 state
    invariant(
      this._lifeCycleState === ReactComponent.LifeCycle.MOUNTED ||
      compositeLifeCycleState === CompositeLifeCycle.MOUNTING,
      'replaceState(...): Can only update a mounted (or mounting) component.'
    );
    // 仅限复合生命周期不为更新 state 或者不为卸载中
    invariant(
      compositeLifeCycleState !== CompositeLifeCycle.RECEIVING_STATE &&
      compositeLifeCycleState !== CompositeLifeCycle.UNMOUNTING,
      'replaceState(...): Cannot update while unmounting component or during ' +
      'an existing state transition (such as within `render`).'
    );

    this._pendingState = completeState;

    // Do not trigger a state transition if we are in the middle of mounting or
    // receiving props because both of those will already be doing this.
    // 若是咱们正在安装或接收道具,请不要触发状态转换,由于这两个道具都已经在进行此操做了。
    // 仅限复合生命周期不为挂载中 或者不为更新 props
    if (compositeLifeCycleState !== CompositeLifeCycle.MOUNTING &&
        compositeLifeCycleState !== CompositeLifeCycle.RECEIVING_PROPS) {
      // 更新复合生命周期为更新 state
      this._compositeLifeCycleState = CompositeLifeCycle.RECEIVING_STATE;

      // ... 执行更新相关操做
      // 置空复合生命周期
      this._compositeLifeCycleState = null;
    }
  },
};
复制代码

生命周期函数方法

那么到此,实现生命周期功能。那么让咱们来看看那些生命周期的钩子都在哪里:spa

// core/ReactCompositeComponent.js
var ReactCompositeComponentInterface = {
  mixins: SpecPolicy.DEFINE_MANY,
  props: SpecPolicy.DEFINE_ONCE,
  getInitialState: SpecPolicy.DEFINE_ONCE,
  render: SpecPolicy.DEFINE_ONCE,
  // ==== Delegate methods ====
  // **一下内容为 Google 翻译**
  // 最初建立组件并即将安装时调用。 这可能有反作用,但必须在 `componentWillUnmount` 中清除此方法建立的任何外部订阅或数据。
  componentWillMount: SpecPolicy.DEFINE_MANY,
  // 在组件已装入并具备DOM表示形式时调用。 可是,没法保证DOM节点位于文档中。 在第一次装入(初始化和渲染)组件时,将此做为操做DOM的机会。
  componentDidMount: SpecPolicy.DEFINE_MANY,
  // 在组件接收新道具以前调用。 使用此做为经过使用 `this.setState` 更新状态来对prop转换做出反应的机会。 目前的道具是经过 `this.props` 访问的。
  // 注意:没有等效的 `componentWillReceiveState` 。传入的道具转换可能会致使状态改变,但状况偏偏相反。若是你须要它,你可能正在寻找 `componentWillUpdate` 。
  componentWillReceiveProps: SpecPolicy.DEFINE_MANY,
  // 在决定是否应该因接收新的道具和状态而更新组件时调用。 当您肯定转换到新的道具和状态不须要更新组件时,能够将此做为 `return false` 的机会。
  shouldComponentUpdate: SpecPolicy.DEFINE_ONCE,
  // 因为从 `this.props` 和 `this.state` 转换为 `nextProps` 和 `nextState` 而致使组件即将更新时调用。使用此做为在更新发生以前执行准备的机会。
  // 注意:您**不能**在此方法中使用 `this.setState()` 。
  componentWillUpdate: SpecPolicy.DEFINE_MANY,
  // 更新组件的DOM表示时调用。 将此做为在更新组件时对DOM进行操做的机会。
  componentDidUpdate: SpecPolicy.DEFINE_MANY,
  // 当组件即将从其父组件中删除并销毁其DOM表示时调用。 使用此做为释听任何外部资源的机会。 注意:没有 `componentDidUnmount` ,由于您的组件将被该点销毁。
  componentWillUnmount: SpecPolicy.DEFINE_MANY,
  // 到此
  updateComponent: SpecPolicy.OVERRIDE_BASE
};
复制代码

生命周期图

来看一下生命周期图:翻译

生命周期图
相关文章
相关标签/搜索