React事务的一些理解
学习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真的每次看都会为内部的实现所惊叹,虽然是前端的一种框架,但还是感觉能学到很多思想和知识,写的不对的地方还希望得到大牛们的指正。