React源代码解析(3):组件的生命周期

栏目: IOS · Android · 发布时间: 5年前

内容简介:组件的生命周期分为二个部分在上一章对于组件的挂载已经做了详细的说明,但是涉及到组件生命周期部分被略过.接下来我将详细的对其说明. 组件的挂载涉及到二个比较重要的生命周期方法对于

组件的生命周期分为二个部分

  1. 组件的挂载
  2. 组件的更新

组件的挂载

在上一章对于组件的挂载已经做了详细的说明,但是涉及到组件生命周期部分被略过.接下来我将详细的对其说明. 组件的挂载涉及到二个比较重要的生命周期方法 componentWillMountcomponentDidMount .

componentWillMount

对于 componentWillMount 这个函数玩过 React 的都知道他是组件 render 之前的触发. 但是如果我再具体点呢. 是在实例之前?还是实例之后?还是构建成真实 dom 之前?还是构建成真实 dom 之前,渲染之前?估计很多人不知道吧.所以在面试的时候无论你对 React 有多熟,还是尽量不要说"精通"二字.(大佬除外)

componentWillMount 是组件更新之前触发,所以直接从 ReactCompositeComponent.mountComponent 里面找

// this.performInitialMount

if (inst.componentWillMount) {
    debugger
    if ("development" !== "production") {
        measureLifeCyclePerf(
            function() {
                return inst.componentWillMount();
            },
            debugID,
            "componentWillMount"
        );
    } else {
        inst.componentWillMount();
    }
    // When mounting, calls to `setState` by `componentWillMount` will set
    // `this._pendingStateQueue` without triggering a re-render.
    if (this._pendingStateQueue) {
        inst.state = this._processPendingState(
            inst.props,
            inst.context
        );
    }
}
复制代码

代码在 performInitialMount 函数里面,所以在实例之后,虚拟 dom 构建真实 dom 之前触发的

componentDidMount

直接看代码吧

var markup;
if (inst.unstable_handleError) {
    markup = this.performInitialMountWithErrorHandling(
        renderedElement,
        hostParent,
        hostContainerInfo,
        transaction,
        context
    );
} else {
    markup = this.performInitialMount(
        renderedElement,
        hostParent,
        hostContainerInfo,
        transaction,
        context
    );
}
if (inst.componentDidMount) {
        if ("development" !== "production") {
            transaction
                .getReactMountReady()
                .enqueue(function() {
                    measureLifeCyclePerf(
                        function() {
                            return inst.componentDidMount();
                        },
                        _this._debugID,
                        "componentDidMount"
                    );
                });
        } else {
            transaction
                .getReactMountReady()
                .enqueue(
                    inst.componentDidMount,
                    inst
                );
        }
    }
复制代码

它是出现在 markup (真实dom)之后.但是肯定不会在这里面执行,因为在 markup 还没插入到 container 里面呢。回顾一下上一章的内容 MountComponentIntoNode 方法 mountComponent 之后还有个 setInnerHTML(container, markup) 只有这个函数执行完之后 componentDidMount 才能执行.

注意 performInitialMount 方法 看看下面的代码

class A extends React.Component {
    render(){
        return <K />
    }
}
<App>
    <A />
</App>
复制代码

this.componentDidMount 的执行顺序是 K-->A--->App . 因为 APP 执行到 this.performInitialMount 就开始深度遍历了.然后执行 AA 又遍历执行 K . K执行完才向上执行. 了解了他们的执行顺序我们看看

transaction
    .getReactMountReady()
    .enqueue(function() {
        measureLifeCyclePerf(
            function() {
                return inst.componentDidMount();
            },
            _this._debugID,
            "componentDidMount"
        );
    });
复制代码

再看看这个 transaction 是在哪里生成的

var transaction = ReactUpdates.ReactReconcileTransaction.getPooled(
    /* useCreateElement */
    !shouldReuseMarkup &&
        ReactDOMFeatureFlags.useCreateElement
);
transaction.perform(
    mountComponentIntoNode,
    null,
    componentInstance,
    container,
    transaction,
    shouldReuseMarkup,
    context
);
复制代码

transactionReact 里面一个非常核心的功能. 出现在很多个地方,不搞清楚 transtion 源代码是没办法读下去的.

事务和队列

看看官方给出的流程图

* <pre>
*                       wrappers (injected at creation time)
*                                      +        +
*                                      |        |
*                    +-----------------|--------|--------------+
*                    |                 v        |              |
*                    |      +---------------+   |              |
*                    |   +--|    wrapper1   |---|----+         |
*                    |   |  +---------------+   v    |         |
*                    |   |          +-------------+  |         |
*                    |   |     +----|   wrapper2  |--------+   |
*                    |   |     |    +-------------+  |     |   |
*                    |   |     |                     |     |   |
*                    |   v     v                     v     v   | wrapper
*                    | +---+ +---+   +---------+   +---+ +---+ | invariants
* perform(anyMethod) | |   | |   |   |         |   |   | |   | | maintained
* +----------------->|-|---|-|---|-->|anyMethod|---|---|-|---|-|-------->
*                    | |   | |   |   |         |   |   | |   | |
*                    | |   | |   |   |         |   |   | |   | |
*                    | |   | |   |   |         |   |   | |   | |
*                    | +---+ +---+   +---------+   +---+ +---+ |
*                    |  initialize                    close    |
*                    +-----------------------------------------+
 * </pre>
var TransactionImpl = {
  reinitializeTransaction: function () {
    this.transactionWrappers = this.getTransactionWrappers();
    if (this.wrapperInitData) {
      this.wrapperInitData.length = 0;
    } else {
      this.wrapperInitData = [];
    }
    this._isInTransaction = false;
  },

  _isInTransaction: false,

  getTransactionWrappers: null,

  isInTransaction: function () {
    return !!this._isInTransaction;
  },

  perform: function (method, scope, a, b, c, d, e, f) {
    !!this.isInTransaction() ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Transaction.perform(...): Cannot initialize a transaction when there is already an outstanding transaction.') : _prodInvariant('27') : void 0;
    var errorThrown;
    var ret;
    try {
      this._isInTransaction = true;
      errorThrown = true;
      this.initializeAll(0);
      ret = method.call(scope, a, b, c, d, e, f);
      errorThrown = false;
    } finally {
      try {
        if (errorThrown) {
          try {
            this.closeAll(0);
          } catch (err) {}
        } else {
          this.closeAll(0);
        }
      } finally {
        this._isInTransaction = false;
      }
    }
    return ret;
  },

  initializeAll: function (startIndex) {
    var transactionWrappers = this.transactionWrappers;
    for (var i = startIndex; i < transactionWrappers.length; i++) {
      var wrapper = transactionWrappers[i];
      try {
        this.wrapperInitData[i] = OBSERVED_ERROR;
        this.wrapperInitData[i] = wrapper.initialize ? wrapper.initialize.call(this) : null;
      } finally {
        if (this.wrapperInitData[i] === OBSERVED_ERROR) {
          try {
            this.initializeAll(i + 1);
          } catch (err) {}
        }
      }
    }
  },

  closeAll: function (startIndex) {
    !this.isInTransaction() ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Transaction.closeAll(): Cannot close transaction when none are open.') : _prodInvariant('28') : void 0;
    var transactionWrappers = this.transactionWrappers;
    for (var i = startIndex; i < transactionWrappers.length; i++) {
      var wrapper = transactionWrappers[i];
      var initData = this.wrapperInitData[i];
      var errorThrown;
      try {
        errorThrown = true;
        if (initData !== OBSERVED_ERROR && wrapper.close) {
          wrapper.close.call(this, initData);
        }
        errorThrown = false;
      } finally {
        if (errorThrown) {
          try {
            this.closeAll(i + 1);
          } catch (e) {}
        }
      }
    }
    this.wrapperInitData.length = 0;
  }
};

module.exports = TransactionImpl;
复制代码

Transaction 的主要作用就是包装一个函数,函数的执行交给 Transaction ,同时Transaction会在函数执行前后执行被注入的 Wrappers ,一个 Wrapper 有二个方法 initializecloseWrapper 是通过 getTransactionWrappers 方法注入的

代码很简单,很容易看明白我就具体说明下每个函数和关键属性的作用

  1. perform 执行注入的函数 fnwrappers ,执行顺序为 initializeAll --> fn --> closeAll
  2. initializeAll 执行所有 Wrapperinitialize 方法
  3. closeAll 执行所有 Wrapperclose 方法
  4. reinitializeTransaction 初始化
  5. isInTransaction 判断事务是否在执行

了解了 Transaction 我们再来仔细分析下上面的代码

var transaction = ReactUpdates.ReactReconcileTransaction.getPooled(
    /* useCreateElement */
    !shouldReuseMarkup &&
        ReactDOMFeatureFlags.useCreateElement
);
复制代码

ReactReconcileTransactiontransition 做了一成包装

ReactReconcileTransaction

var TRANSACTION_WRAPPERS = [
    SELECTION_RESTORATION,
    EVENT_SUPPRESSION,
    ON_DOM_READY_QUEUEING
];

if ("development" !== "production") {
    TRANSACTION_WRAPPERS.push({
        initialize:
            ReactInstrumentation.debugTool.onBeginFlush,
        close: ReactInstrumentation.debugTool.onEndFlush
    });
}

/**
 * Currently:
 * - The order that these are listed in the transaction is critical:
 * - Suppresses events.
 * - Restores selection range.
 *
 * Future:
 * - Restore document/overflow scroll positions that were unintentionally
 *   modified via DOM insertions above the top viewport boundary.
 * - Implement/integrate with customized constraint based layout system and keep
 *   track of which dimensions must be remeasured.
 *
 * @class ReactReconcileTransaction
 */
 
function ReactReconcileTransaction(useCreateElement) {
    this.reinitializeTransaction();
    this.renderToStaticMarkup = false;
    this.reactMountReady = CallbackQueue.getPooled(
        null
    );
    this.useCreateElement = useCreateElement;

}

var Mixin = {
    /**
     * @see Transaction
     * @abstract
     * @final
     * @return {array<object>} List of operation wrap procedures.
     *   TODO: convert to array<TransactionWrapper>
     */
    getTransactionWrappers: function() {
        return TRANSACTION_WRAPPERS;
    },

    /**
     * @return {object} The queue to collect `onDOMReady` callbacks with.
     */
    getReactMountReady: function() {
        return this.reactMountReady;
    },

    /**
     * @return {object} The queue to collect React async events.
     */
    getUpdateQueue: function() {
        return ReactUpdateQueue;
    },

    /**
     * Save current transaction state -- if the return value from this method is
     * passed to `rollback`, the transaction will be reset to that state.
     */
    checkpoint: function() {
        // reactMountReady is the our only stateful wrapper
        return this.reactMountReady.checkpoint();
    },

    rollback: function(checkpoint) {
        this.reactMountReady.rollback(checkpoint);
    },

    /**
     * `PooledClass` looks for this, and will invoke this before allowing this
     * instance to be reused.
     */
    destructor: function() {
        CallbackQueue.release(this.reactMountReady);
        this.reactMountReady = null;
    }
};
复制代码

getTransactionWrappers 方法里面返回的是 TRANSACTION_WRAPPERS 他的值有4个也就是说注入了四个 Wrapper 。具体看看 ON_DOM_READY_QUEUEING 这个 Wraper ;

var ON_DOM_READY_QUEUEING = {
    /**
     * Initializes the internal `onDOMReady` queue.
     */
    initialize: function() {
        this.reactMountReady.reset();
    },

    /**
     * After DOM is flushed, invoke all registered `onDOMReady` callbacks.
     */
    close: function() {
        this.reactMountReady.notifyAll();
    }
};
复制代码

this.reactMountReady 是一个队列, 在组件构建真实 dom 之后

transaction
    .getReactMountReady()
    .enqueue(function() {
        measureLifeCyclePerf(
            function() {
                return inst.componentDidMount();
            },
            _this._debugID,
            "componentDidMount"
        );
    });
复制代码

会将 componentDidMount 方法push进入队列里面. 而 mountComponentIntoNode (插入到了 document 中了)执行完毕之后会执行 ON_DOM_READY_QUEUEING.close 方法也就是 this.reactMountReady.notifyAll() 方法,释放队列中所有的元素。

componentDidMount 是通过一个队列来维护的,因为队列是 先进先出 的.而最里层的组件是最新执行!


以上所述就是小编给大家介绍的《React源代码解析(3):组件的生命周期》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

打火机与公主裙·荒草园

打火机与公主裙·荒草园

Twentine / 青岛出版社 / 2017-3 / 36.00元

“如果人临死前真有走马灯这个环节,她大概会是我这辈子见的最后一人。” 从青涩的校园时代里一抹明亮的金,到厮杀的职场中那化不开的黑,李峋就像荒芜之地的一株野草,受到再大的挫折依然固执地生长。 如果说朱韵从前的生活一直维持着表面的顺风顺水,平静安和,那李峋的出现则打破了这一切。他是她生命中第一次,也是唯一一次的冒险。 在外人眼里李峋嚣张而轻蔑,只有朱韵懂得他心中那片自留地,自愿成为孤......一起来看看 《打火机与公主裙·荒草园》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

在线进制转换器
在线进制转换器

各进制数互转换器

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码