React 源码深度解读(八):事务 - Part 2
前言
React 是一个十分庞大的库,由于要同时考虑 ReactDom 和 ReactNative ,还有服务器渲染等,导致其代码抽象化程度很高,嵌套层级非常深,阅读其源码是一个非常艰辛的过程。在学习 React 源码的过程中,给我帮助最大的就是这个系列文章,于是决定基于这个系列文章谈一下自己的理解。本文会大量用到原文中的例子,想体会原汁原味的感觉,推荐阅读原文。
本系列文章基于 React 15.4.2 ,以下是本系列其它文章的传送门:
React 源码深度解读(一):首次 DOM 元素渲染 - Part 1
React 源码深度解读(二):首次 DOM 元素渲染 - Part 2
React 源码深度解读(三):首次 DOM 元素渲染 - Part 3
React 源码深度解读(四):首次自定义组件渲染 - Part 1
React 源码深度解读(五):首次自定义组件渲染 - Part 2
React 源码深度解读(六):依赖注入
React 源码深度解读(七):事务 - Part 1
React 源码深度解读(八):事务 - Part 2
React 源码深度解读(九):单个元素更新
React 源码深度解读(十):Diff 算法详解
正文
上一篇文章介绍了 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 相关的内容就讲完了,下一篇开始介绍真正的更新操作。