组件的生命周期分为二个部分
组件的挂载
组件的更新
组件的挂载
在上一章对于组件的挂载已经做了详细的说明, 但是涉及到组件生命周期部分被略过. 接下来我将详细的对其说明. 组件的挂载涉及到二个比较重要的生命周期方法 componentWillMount 和 componentDidMount.
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 就开始深度遍历了. 然后执行 A,A 又遍历执行 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
- );
transaction 是 React 里面一个非常核心的功能. 出现在很多个地方, 不搞清楚 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 有二个方法 initialize 和 close.Wrapper 是通过 getTransactionWrappers 方法注入的
代码很简单, 很容易看明白我就具体说明下每个函数和关键属性的作用
perform 执行注入的函数 fn 和 wrappers, 执行顺序为 initializeAll--> fn -->closeAll
initializeAll 执行所有 Wrapper 的 initialize 方法
closeAll 执行所有 Wrapper 的 close 方法
reinitializeTransaction
初始化
isInTransaction 判断事务是否在执行
了解了 Transaction 我们再来仔细分析下上面的代码
- var transaction = ReactUpdates.ReactReconcileTransaction.getPooled(
- /* useCreateElement */
- !shouldReuseMarkup &&
- ReactDOMFeatureFlags.useCreateElement
- );
ReactReconcileTransaction 对 transition 做了一成包装
- 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 是通过一个队列来维护的, 因为队列是先进先出的. 而最里层的组件是最新执行!
来源: https://juejin.im/post/5c048aa351882558da6fec84