事务机制
事务机制是React中非常重要的机制,这里的事务区别于数据库中的事务,它是一个借用的概念,与其说是事务,更像是拦截器(Interceptor)。React中的事务进行了两层“抽象“,一层是抽象的事务接口定义,其代码都在src/utils/Transaction.js中,另外一层是基于抽象的Transaction定义了在React框架中通用的事务流程ReactReconcileTransaction,更确切的说,Transaction是接口,而ReactReconcileTransaction则是其具体实现。
Transaction
关于事务机制的设计,React源代码中给出了一份草图描述其工作原理,如下所示
/* wrappers (injected at creation time)
* + +
* | |
* +-----------------|--------|--------------+
* | v | |
* | +---------------+ | |
* | +--| wrapper1 |---|----+ |
* | | +---------------+ v | |
* | | +-------------+ | |
* | | +----| wrapper2 |--------+ |
* | | | +-------------+ | | |
* | | | | | |
* | v v v v | wrapper
* | +---+ +---+ +---------+ +---+ +---+ | invariants
* perform(anyMethod) | | | | | | | | | | | | maintained
* +----------------->|-|---|-|---|-->|anyMethod|---|---|-|---|-|-------->
* | | | | | | | | | | | |
* | | | | | | | | | | | |
* | | | | | | | | | | | |
* | +---+ +---+ +---------+ +---+ +---+ |
* | initialize close |
* +-----------------------------------------+
*/
这张图形象详细地描述了整个抽象事务的工作过程。简言之,就是在执行perform(或者其他方法)前,执行若干个wrapper函数,以保证perform执行过程中,不会出现数据错乱等问题。
事务执行分为3个阶段,初始化阶段,执行阶段和关闭阶段。在初始化阶段,每一个wrapper的initialize方法都会被调用并执行,为了方便close阶段的数据共享,initialize阶段的执行结果会被保存,并且在相应的close方法执行时会作为参数传入。 具体到代码层面,transaction的mixin中定义了下面几个重要方法,除了getTransactionWrapper没有具体实现。
getTransactionWrappers
这是一个抽象函数,Transaction类中没有其对应的方法体,继承Transaction的类,必须给出该方法的具体实现,ReactReconcileTransaction就实现了这个方法。
peform
事务执行的核心方法,它接受7个参数,第一个参数是要在事务中执行的method函数,第二个参数时执行method函数的scope,后面的参数是执行method函数的传参。
React限制了method能够接受的参数数量不能超过5个**。**5个参数的限制在缓冲池机制中也有体现,或许是facebook的程序员认为,一个函数的参数以不超过5个为宜吧。
在method执行之前,React会通过调用initializeAll方法执行所有wrapper的initialize方法;method在try-catch语句块中执行;closeAll方法则会在finally中被调用。
跨阶段数据传递
有时侯事务初始化阶段的结果会需要被传递到close阶段使用,例如在事件事务处理时,某些全局事件的监听状态,需要在initialize时被关闭,而等到close阶段时,恢复成原来的状态(打开或关闭)。为了能够满足这样的数据传递需求,React会将每个wrapper函数的initialize执行结果保存到wrapperIniData中,等到执行close方法时,再把数据传递过去,其关键源码节选如下:
保存initialize执行结果
var transactionWrappers = this.transactionWrappers;
var wrapperInitTimes = this.timingMetrics.wrapperInitTimes;
var err = null;
for (var i = 0; i < transactionWrappers.length; i++) {
var initStart = Date.now();
var wrapper = transactionWrappers[i];
try {
this.wrapperInitData[i] =
wrapper.initialize ? wrapper.initialize.call(this) : null;
} catch (initErr) {
err = err || initErr; // Remember the first error.
this.wrapperInitData[i] = Transaction.OBSERVED_ERROR;
} finally {
var curInitTime = wrapperInitTimes[i];
var initEnd = Date.now();
wrapperInitTimes[i] = (curInitTime || 0) + (initEnd - initStart);
}
}
if (err) {
throw err;
}
传递数据
for (var i = 0; i < transactionWrappers.length; i++) {
var wrapper = transactionWrappers[i];
var closeStart = Date.now();
var initData = this.wrapperInitData[i];
try {
if (initData !== Transaction.OBSERVED_ERROR) {
wrapper.close && wrapper.close.call(this, initData);
}
} catch (closeErr) {
err = err || closeErr; // Remember the first error.
} finally {
var closeEnd = Date.now();
var curCloseTime = wrapperCloseTimes[i];
wrapperCloseTimes[i] = (curCloseTime || 0) + (closeEnd - closeStart);
}
}
ReactReconcileTransaction
Transaction中定义了抽象的事务处理过程,而具体到React的事务处理过程,则是通过ReactReconcileTransaction来完成的。
ReactReconcileTransaction中除了mix了抽象Transaction的mixin,还额外定义了自己的mixin,其中包含3个主要方法。
getTransactionWrapper
这是实现抽象事务必须实现的一个方法,该方法返回事务处理的所有wrapper函数。React事务处理的所有Wrapper都定义在TRANSACTION_WRAPPERS中(src/core/ReactReconcileTransaction.js),主要包括3个wrapper,分别是
SELECTION_RESTORATION
这是针对选区处理的wrapper,它会将当前选中的区域信息存储下来,在事务执行完毕之后再还原选区信息。
EVENT_SUPPRESSION
该wrapper会在事务处理时,初始化阶段暂停所有的事件监听,并将原始的事件监听状态传递到close阶段,等到close阶段再将事件监听的状态还原为初始值。
ON_DOM_READY_QUEUEING
该wrapper主要负责处理onDOMReady的回调,比如在执行挂载时,componentDidMount就是通过事务处理完毕后close阶段中,ON-DOM-READY-QUEUEING执行reactOnDOMReady.notifyAll实现的。
destructor
缓冲池对象的析构函数,内容很简单,释放了reactOnDOMReady资源,使之返回到缓冲池。