React原始碼解析(3):元件的生命週期
元件的生命週期分為二個部分
- 元件的掛載
- 元件的更新
元件的掛載
在上一章對於元件的掛載已經做了詳細的說明,但是涉及到元件生命週期部分被略過.接下來我將詳細的對其說明. 元件的掛載涉及到二個比較重要的生命週期方法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
是通過一個佇列來維護的,因為佇列是先進先出的.而最裡層的元件是最新執行!
元件的更新this.setState
緩緩,明天更新!