学习 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 的阶段会将 isBatchingUpdates 设置为 flase,以及调用 flushBatchedUpdates 方法进行将至更新。
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
});
}
总结
ser-gold-cdn.xitu.io/2019/1/26/1688a9f6bef4dac6?w=2134&h=912&f=png&s=220759)
总之,React 真的每次看都会为内部的实现所惊叹,虽然是前端的一种框架,但还是感觉能学到很多思想和知识,写的不对的地方还希望得到大牛们的指正。