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
};
複製代碼

生命週期圖

來看一下生命週期圖:翻譯

生命週期圖
相關文章
相關標籤/搜索