一、前言
上一篇文章介紹了 transaction 的基本概念和用法。今天我們將講解在更新過程中,React 是如何通過多個 transacion 之間的協作,來有效組織代碼的。
二、ReactUpdatesFlushTransaction
前文講到ReactDefaultBatchingStrategy
close 的時候,會調用ReactUpdates.flushBatchedUpdates
:
PooledClass.addPoolingTo(ReactUpdatesFlushTransaction);
var flushBatchedUpdates = function () {
while (dirtyComponents.length || asapEnqueued) {
if (dirtyComponents.length) {
var transaction = ReactUpdatesFlushTransaction.getPooled();
transaction.perform(runBatchedUpdates, null, transaction);
ReactUpdatesFlushTransaction.release(transaction);
}
if (asapEnqueued) {
asapEnqueued = false;
var queue = asapCallbackQueue;
asapCallbackQueue = CallbackQueue.getPooled();
queue.notifyAll();
CallbackQueue.release(queue);
}
}
};
這裏又調用了另一個 transaction 來處理後續的流程,有所不同的是 transaction 的創建不是直接 new,而是調用getPooled
方法。這個方法是通過前面的PooledClass.addPoolingTo
注入到ReactUpdatesFlushTransaction
中的,如果對這個步驟感興趣可以看看這篇文章。下面來看ReactUpdatesFlushTransaction
的內容:
Object.assign(
ReactUpdatesFlushTransaction.prototype,
Transaction,
{
getTransactionWrappers: function () {
return TRANSACTION_WRAPPERS;
},
destructor: function () {
this.dirtyComponentsLength = null;
CallbackQueue.release(this.callbackQueue);
this.callbackQueue = null;
ReactUpdates.ReactReconcileTransaction.release(this.reconcileTransaction);
this.reconcileTransaction = null;
},
perform: function (method, scope, a) {
return Transaction.perform.call(
this,
this.reconcileTransaction.perform,
this.reconcileTransaction,
method,
scope,
a
);
},
}
);
var TRANSACTION_WRAPPERS = [NESTED_UPDATES, UPDATE_QUEUEING];
var NESTED_UPDATES = {
initialize: function () {
this.dirtyComponentsLength = dirtyComponents.length;
},
close: function () {
if (this.dirtyComponentsLength !== dirtyComponents.length) {
// Additional updates were enqueued by componentDidUpdate handlers or
// similar; before our own UPDATE_QUEUEING wrapper closes, we want to run
// these new updates so that if A's componentDidUpdate calls setState on
// B, B will update before the callback A's updater provided when calling
// setState.
dirtyComponents.splice(0, this.dirtyComponentsLength);
flushBatchedUpdates();
} else {
dirtyComponents.length = 0;
}
},
};
var UPDATE_QUEUEING = {
initialize: function () {
this.callbackQueue.reset();
},
close: function () {
this.callbackQueue.notifyAll();
},
};
ReactUpdatesFlushTransaction
覆蓋了原型鏈上的perform
方法,不是直接調用 callback,而是嵌套調用了this.reconcileTransaction.perform
,在將 callback 透傳給reconcileTransaction
的perform
。這裏的reconcileTransaction
也開啓了實例池。
這裏要注意下NESTED_UPDATES
這個 wrapper,如果dirtyComponents
的數量跟 transaction 開始的時候不一樣,它又會遞歸調用flushBatchedUpdates
,直到dirtyComponents
不再變化爲止。UPDATE_QUEUEING
這個 wrapper 暫時先忽略。
目前爲止的調用關係如下:
三、ReactReconcileTransaction
ReactReconcileTransaction
是一個普通的 transaction,定義了一些 DOM 操作相關的 wrapper:
function ReactReconcileTransaction(useCreateElement: boolean) {
this.reinitializeTransaction();
this.renderToStaticMarkup = false;
this.reactMountReady = CallbackQueue.getPooled(null);
this.useCreateElement = useCreateElement;
}
var Mixin = {
getTransactionWrappers: function() {
return TRANSACTION_WRAPPERS;
},
...
destructor: function() {
CallbackQueue.release(this.reactMountReady);
this.reactMountReady = null;
},
};
Object.assign(ReactReconcileTransaction.prototype, Transaction, Mixin);
PooledClass.addPoolingTo(ReactReconcileTransaction);
var TRANSACTION_WRAPPERS = [
SELECTION_RESTORATION,
EVENT_SUPPRESSION,
ON_DOM_READY_QUEUEING,
];
var SELECTION_RESTORATION = {
/**
* @return {Selection} Selection information.
*/
initialize: ReactInputSelection.getSelectionInformation,
/**
* @param {Selection} sel Selection information returned from `initialize`.
*/
close: ReactInputSelection.restoreSelection,
};
/**
* Suppresses events (blur/focus) that could be inadvertently dispatched due to
* high level DOM manipulations (like temporarily removing a text input from the
* DOM).
*/
var EVENT_SUPPRESSION = {
/**
* @return {boolean} The enabled status of `ReactBrowserEventEmitter` before
* the reconciliation.
*/
initialize: function() {
var currentlyEnabled = ReactBrowserEventEmitter.isEnabled();
ReactBrowserEventEmitter.setEnabled(false);
return currentlyEnabled;
},
/**
* @param {boolean} previouslyEnabled Enabled status of
* `ReactBrowserEventEmitter` before the reconciliation occurred. `close`
* restores the previous value.
*/
close: function(previouslyEnabled) {
ReactBrowserEventEmitter.setEnabled(previouslyEnabled);
},
};
/**
* Provides a queue for collecting `componentDidMount` and
* `componentDidUpdate` callbacks during the transaction.
*/
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();
},
};
這三個 wrapper 的作用註釋都講得很清楚了,不再贅述。值得一提的是這裏perform
的 callback 是ReactUpdatesFlushTransaction
透傳過來的ReactUpdate.runBatchedUpdates
。
目前爲止的調用關係如下:
四、總結
到此爲止,transaction 相關的內容就講完了,下一篇開始介紹真正的更新操作。