學習React有一段時間了,剛接觸不久對於React神奇的操作很好奇,迫不及待看了原始碼看過幾遍原始碼總是一知半解,最近有時間再次學習React的相關知識,比如setState
, componentDidMount
等,意識到了之前被忽略提及的知識點,那就是React內部的事務,個人覺得事務很重要,生命週期中的componentWillMount
,componentDidMount
, componentDidUpdate
以及在一些生命週期中進行setState出現的一些出乎自己認知的結果,都和事務有很大的關係。
何為事務?
根據維基百科的解釋: 提供獨立可靠的恢復機制,保證出錯時資料的一致性,並且不同事務之間互相獨立。
事務一般在資料庫中使用的比較多,能保證出錯的時候進行rollbakc恢復。在React原始碼中作者給出了事務的一張明細圖能夠幫助較好的理解。React內部的事務分為三個階段initialize, method以及close階段,會在開始和結束時候分別遍歷transactionWrapper內部的所有初始化方法和close方法。
有哪些事務?
React內部有個事務物件,能夠適配不同型別的事務:
Transaction.perform.call(this, this.reconcileTransaction.perform, this.reconcileTransaction, method, scope, a)
複製程式碼
主要先介紹2種事務,分別對應虛擬生命週期事務,狀態更新時的事務
批量更新事務(ReactDefaultBatchingStrategyTransaction)
初始化進行元件掛載的時候會進行批量更新,批量更新方法會將ReactDefaultBatchingStrategy
物件中的isBatchingUpdates
設定為true,這也將導致後續加入的setState只會加入dirtyComponents
中,在最後事務close的時候進行狀態合併,這也解釋了為何在componentDidMount中寫多個setState,最後輸出的狀態不是意料值。
var ReactDefaultBatchingStrategy = {
isBatchingUpdates: false,
/**
* Call the provided function in a context within which calls to `setState`
* and friends are batched such that components aren't updated unnecessarily.
*/
batchedUpdates: function (callback, a, b, c, d, e) {
var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates;
ReactDefaultBatchingStrategy.isBatchingUpdates = true;
// The code is written this way to avoid extra allocations
if (alreadyBatchingUpdates) {
return callback(a, b, c, d, e);
} else {
return transaction.perform(callback, null, a, b, c, d, e);
}
}
};
複製程式碼
// 將新的partialState加入到_pendingStateQueue以後的元件加到dirtyComponents
function enqueueUpdate(component) {
ensureInjected();
if (!batchingStrategy.isBatchingUpdates) {
batchingStrategy.batchedUpdates(enqueueUpdate, component);
return;
}
dirtyComponents.push(component);
if (component._updateBatchNumber == null) {
component._updateBatchNumber = updateBatchNumber + 1;
}
}
複製程式碼
首先是初始化遍歷執行emptyFunction,然後執行內部的被包裝的方法,這裡是batchedMountComponentIntoNode
,顧名思義這個就是將虛擬DOM插入到真實節點下的方法,具體方法涉及到類別判斷來例項化不同型別的元件,遞迴插入等等不在本章的討論範圍,不過有一點需要注意的是在這個方法內部進行已新事物,我稱之為生命週期事務
(ReactReconcileTransaction)。在最後close的階段呼叫flushBatchedUpdates
方法進行將至更新完成,以及會將isBatchingUpdates
設定為flase,這兩個close先後順序有一定講究,這也導致在這個事件或者componentDidMount中isBatchingUpdates的值始終為true,在最後結束才會為false。
var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES];
var RESET_BATCHED_UPDATES = {
initialize: emptyFunction,
close: function () {
ReactDefaultBatchingStrategy.isBatchingUpdates = false;
}
};
var FLUSH_BATCHED_UPDATES = {
initialize: emptyFunction,
close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates)
};
複製程式碼
更新事務(ReactUpdateFlushTransaction)
而上述說到的flushBatchedUpdates方法內部再次呼叫了一個新事務
該事物我稱之為更新事務,主要和setState有關,過程中會呼叫runBatchedUpdates, 將callBack函式加入到佇列中,並將_pendingCallbacks的引用清空,關閉的時候的close方法主要是對dirtyComponents清空以及setState中回撥函式的通知
var TRANSACTION_WRAPPERS = [NESTED_UPDATES, UPDATE_QUEUEING];
var NESTED_UPDATES = {
initialize: function () {
this.dirtyComponentsLength = dirtyComponents.length;
},
close: function () {
if (this.dirtyComponentsLength !== dirtyComponents.length) {
dirtyComponents.splice(0, this.dirtyComponentsLength);
flushBatchedUpdates();
} else {
dirtyComponents.length = 0;
}
}
};
var UPDATE_QUEUEING = {
initialize: function () {
this.callbackQueue.reset();
},
close: function () {
this.callbackQueue.notifyAll();
}
};
複製程式碼
生命週期事務(ReactReconcileTransaction)
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);
}
}
複製程式碼
在這裡開啟了一個事務,componentDidMount被放入了callbacks佇列中,當生命週期結束的時候會遍歷事務中的close方法,其中就有notifyAll方法遍歷callbacks進行輸出,而這裡的callback方法即為componentDidMount
或者componentDidUpdate
。
var TRANSACTION_WRAPPERS = [SELECTION_RESTORATION, EVENT_SUPPRESSION, ON_DOM_READY_QUEUEING];
var EVENT_SUPPRESSION = {
initialize: function () {
var currentlyEnabled = ReactBrowserEventEmitter.isEnabled();
ReactBrowserEventEmitter.setEnabled(false);
return currentlyEnabled;
},
// 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();
}
};
if ("development" !== 'production') {
TRANSACTION_WRAPPERS.push({
initialize: ReactInstrumentation.debugTool.onBeginFlush,
close: ReactInstrumentation.debugTool.onEndFlush
});
}
複製程式碼
總結
總之,React真的每次看都會為內部的實現所驚歎,雖然是前端的一種框架,但還是感覺能學到很多思想和知識,寫的不對的地方還希望得到大牛們的指正。