英文注释翻译
React 中的 Transaction 创建一个黑盒环境来对方法进行封装,它能够封装任何方法,以便在调用方法之前和之后维护某些不变量(即使在调用封装的方法时抛出异常)。
任何实例化事务的人都可以在创建时提供不变量的实施者。“Transaction”类本身将为您提供一个额外的自动不变量(_isInTransaction)——任何事务实例在已经运行时不应该运行这个不变量。您通常会创建一个“事务”的单一实例,以便多次重用,该实例可能用于包装几个不同的方法。包装器非常简单——它们只需要实现两个方法。
使用范围
- 保存‘调和’前后的输入选择范围。恢复选择,即使在发生意外错误的情况下。
- 重排Dom时,禁用事件,避免引发多余的 blur/focus 事件,同时确保重排之后重新激活事件系统
- work线程调和后,由主线程更新UI
- 渲染完成之后,调用所有的componentDidUpdate
- (Future case)包装“ReactWorker”队列的特定刷新,以保存“scrollTop”(自动滚动感知DOM)。
- (Future case)DOM更新前后的布局计算。
事务性插件API
- 'initialize': 具有“初始化”方法的模块,该方法返回任何预计算。
- 'close': 以及接受预计算的“close”方法。“close”在包装的流程完成或失败时调用。
自定义Transaction: 通过将Tracaction中的属性通过Object.assign添加到自定义transaction的原型链中
个人理解
Transaction 对我们所要执行的方法进行一个封装,创建了一个黑盒环境。每个 Transaction 都会被一个或多个 wrapper 包裹,每一个 wrapper 都包含两个属性,一个是
initialize
一个是close
,当 transaction执行的时候,首先会执行所有的initialize
并添加异常机制,之后执行我们包装的方法,最后在执行所有的close
并添加异常机制。属性概述
_isInTransaction
:Transaction是否在运行
reinitializeTransaction
:初始化Transaction
getTransactionWrappers
:获取包裹的wrapper
isInTransaction
:判断当前是否在运行的方法
perform
:Transaction的核心方法,用来执行包裹的method
inistializeAll
:执行所有wrapper
的initialize
closeAll
:执行所有wrapper
的close
自定义Transaction
当我们衍生出一个自定义Transaction时,只需要使用Object.assign将Transaction的所有方法添加到我们自定义的Transaction上,然后重写
getTransactionWrappers
方法。
官方给的一个图很形象
wrappers
是在Transaction被创建时注入的,实质上是通过上面说的通过Object.assign方法重写getTransactionWrappers
方法得到包裹的wrappers
,当运行perform
方法时,首先执行的是所有的initialize
之后是method
最后是所有的close
Transaction
reinitializeTransaction
用于 Transaction 初始化,在实例化Transaction时,都执行了这个方法。
// Transaction.js
reinitializeTransaction: function () {
this.transactionWrappers = this.getTransactionWrappers();
if (this.wrapperInitData) {
this.wrapperInitData.length = 0;
} else {
this.wrapperInitData = [];
}
this._isInTransaction = false;
},
通过调用getTransactionWrappers
方法,给Transaction实例添加transactionWrappers
属性,之后附加wrapperInitData
为空数组,并将运行标志_isInTransaction
设为false
。
其中wrapperInitData
数组是用来存储initialize
的返回值的,用原文注释中的说法就是,存储initialize
返回的预计算数据,在close
方法中需要用到这些预计算,实质上就是用来判断是否出现了异常。
getTransactionWrappers
// Transaction.js
/**
* @abstract
* @return {Array} Array of transaction wrappers.
*/
getTransactionWrappers: null,
这个属性用来获取Transaction外部的wrappers
,在定义Transaction时,会通过Object.assign方法重写该属性
isInTransaction
// Transaction.js
isInTransaction: function () {
return !!this._isInTransaction;
},
判断当前Transaction
是否在执行,使用!!
两个感叹号,是可以做类型判断
perform
// Transaction.js
perform: function (method, scope, a, b, c, d, e, f) {
/* eslint-enable space-before-function-paren */
!!this.isInTransaction() ? /**/
var errorThrown;
var ret;
try {
this._isInTransaction = true;
// Catching errors makes debugging more difficult, so we start with
// errorThrown set to true before setting it to false after calling
// close -- if it's still set to true in the finally block, it means
// one of these calls threw.
// 先设置为 true 在调用 close 之后设置 false,如果最后还是 true 的话 那么中途出现了异常
errorThrown = true;
this.initializeAll(0);
ret = method.call(scope, a, b, c, d, e, f);
errorThrown = false;
} finally {
try {
if (errorThrown) {
// If `method` throws, prefer to show that stack trace over any thrown
// by invoking `closeAll`.
try {
this.closeAll(0);
} catch (err) {}
} else {
// Since `method` didn't throw, we don't want to silence the exception
// here.
this.closeAll(0);
}
} finally {
this._isInTransaction = false;
}
}
return ret;
},
Transaction
的核心方法,用来执行包裹的method
。首先会判断当前
Transaction
是否在运行,是的话会报错
之后会将_isInTransaction
设为true
,表明该Transaction在执行
调用
initializeAll(0)
参数0
表示从第一个wrapper开始运行,0
表示wrappers数组的下标errorThrown
用来判断执行method
期间是否出现了异常执行
method
执行所有的
close
将
_isInTransaction
设为false
表明Transaction
执行完成返回
method
的返回值
initializeAll
// Transaction.js
initializeAll: function (startIndex) {
var transactionWrappers = this.transactionWrappers;
for (var i = startIndex; i < transactionWrappers.length; i++) {
var wrapper = transactionWrappers[i];
try {
// Catching errors makes debugging more difficult, so we start with the
// OBSERVED_ERROR state before overwriting it with the real return value
// of initialize -- if it's still set to OBSERVED_ERROR in the finally
// block, it means wrapper.initialize threw.
this.wrapperInitData[i] = OBSERVED_ERROR;
this.wrapperInitData[i] = wrapper.initialize ? wrapper.initialize.call(this) : null;
} finally {
if (this.wrapperInitData[i] === OBSERVED_ERROR) {
// The initializer for wrapper i threw an error; initialize the
// remaining wrappers but silence any exceptions from them to ensure
// that the first error is the one to bubble up.
try {
this.initializeAll(i + 1);
} catch (err) {}
}
}
}
},
执行所有的initialize
方法,这边做了异常处理,异常逻辑为:将对应的wrapperInitData
设为OBSERVED_ERROR
,之后将initialize
的返回值设为新的wrapperInitData
,最后判断wrapperInitData
的值,如果仍为OBSERVED_ERROR
表明在执行initialize的过程中发生了异常。
closeAll
执行所有的close,其中的异常处理机制和initializeAll
相类似
closeAll: function (startIndex) {
!this.isInTransaction() ? /**/
var transactionWrappers = this.transactionWrappers;
for (var i = startIndex; i < transactionWrappers.length; i++) {
var wrapper = transactionWrappers[i];
var initData = this.wrapperInitData[i];
var errorThrown;
try {
// Catching errors makes debugging more difficult, so we start with
// errorThrown set to true before setting it to false after calling
// close -- if it's still set to true in the finally block, it means
// wrapper.close threw.
errorThrown = true;
if (initData !== OBSERVED_ERROR && wrapper.close) {
wrapper.close.call(this, initData);
}
errorThrown = false;
} finally {
if (errorThrown) {
// The closer for wrapper i threw an error; close the remaining
// wrappers but silence any exceptions from them to ensure that the
// first error is the one to bubble up.
try {
this.closeAll(i + 1);
} catch (e) {}
}
}
}
this.wrapperInitData.length = 0;
}
执行完之后将wrapperInitData
清空。
ReactDefaultBatchingStrategyTransaction
在ReactDefaultBatchingStrategy.js
中定义了一个默认批处理策略事务。
// ReactDefaultBatchingStrategy.js
function ReactDefaultBatchingStrategyTransaction() {
this.reinitializeTransaction();
}
_assign(ReactDefaultBatchingStrategyTransaction.prototype, Transaction, {
getTransactionWrappers: function () {
return TRANSACTION_WRAPPERS;
}
});
通过Object.assign
将Transaction的属性添加到自定义Transaction的原型链中,并重写getTransactionWrappers
方法,并且在实例化时,都会调用reinitializeTransaction
方法进行初始化,这就是自定义一个Transaction的过程。
var RESET_BATCHED_UPDATES = {
initialize: emptyFunction,
close: function () {
ReactDefaultBatchingStrategy.isBatchingUpdates = false;
}
};
var FLUSH_BATCHED_UPDATES = {
initialize: emptyFunction,
close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates)
};
var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES];
这个Transaction被两个wrapper包裹。他们的initialize
方法都是空函数,那么关注点就转移到close
方法,在该Transaction方法直接完之后,首先会将ReactDefaultBatchingStrategy.isBatchingUpdates
设为false
,之后就调用ReactUpdates.flushBatchedUpdates
刷新更新队列。
ReactReconcileTransaction
调和事务
/**
* Currently:
* - The order that these are listed in the transaction is critical:
* - Suppresses events.
* - Restores selection range.
*
* Future:
* - Restore document/overflow scroll positions that were unintentionally
* modified via DOM insertions above the top viewport boundary.
* - Implement/integrate with customized constraint based layout system and keep
* track of which dimensions must be remeasured.
*
* @class ReactReconcileTransaction
*/
function ReactReconcileTransaction(useCreateElement) {
this.reinitializeTransaction();
// Only server-side rendering really needs this option (see
// `ReactServerRendering`), but server-side uses
// `ReactServerRenderingTransaction` instead. This option is here so that it's
// accessible and defaults to false when `ReactDOMComponent` and
// `ReactDOMTextComponent` checks it in `mountComponent`.`
this.renderToStaticMarkup = false;
this.reactMountReady = CallbackQueue.getPooled(null);
this.useCreateElement = useCreateElement;
}
构造函数内部包含了三个属性
renderToStaticMarkup
:只有在服务器端运行时用到reactMountReady
:通过CallbackQueue.getPooled
获取,实质上返回一个CallbackQueue
实例useCreateElement
:值为构造函数实例化时传入的参数
调和事务的属性稍微多了一点,通过混合Mixin
注入原型链
var Mixin = {
/**
* @see Transaction
* @abstract
* @final
* @return {array
这边提到了ReactUpdateQueue
,也是一个重点。
外部包含了三个wrapper
var TRANSACTION_WRAPPERS = [SELECTION_RESTORATION, EVENT_SUPPRESSION, ON_DOM_READY_QUEUEING];
// 确保下拉框选择的数据在transaction后仍被选择
var SELECTION_RESTORATION = {
/**
* @return {Selection} Selection information.
*/
initialize: ReactInputSelection.getSelectionInformation,
/**
* @param {Selection} sel Selection information returned from `initialize`.
*/
close: ReactInputSelection.restoreSelection
};
// 抑制可能由于高级DOM操作(如临时从DOM中删除文本输入)而意外分派的事件(blur/focus)。
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);
}
};
// 提供一个队列,来收集在 componentDidMount 和 componentDidUpdate 回调
// 在执行事务期间,遇到这两个生命周期,会将回调押入到队列中。在close的时候在去执行
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();
}
};
_assign(ReactReconcileTransaction.prototype, Transaction, Mixin);
PooledClass.addPoolingTo(ReactReconcileTransaction);
再往下就是这两句,第一句是将Transaction
,Mixin
都添加到原型链中。
第二句是调用PooledClass.addPoolingTo
用到了PoolClass
属性。
PoolClass
PoolClass
主要目的就是减少内存消耗。
// PoolClass.js
var PooledClass = {
addPoolingTo: addPoolingTo,
oneArgumentPooler: oneArgumentPooler,
twoArgumentPooler: twoArgumentPooler,
threeArgumentPooler: threeArgumentPooler,
fourArgumentPooler: fourArgumentPooler
};
// PoolClass.js
var addPoolingTo = function (CopyConstructor, pooler) {
// Casting as any so that flow ignores the actual implementation and trusts
// it to match the type we declared
var NewKlass = CopyConstructor;
NewKlass.instancePool = [];
NewKlass.getPooled = pooler || DEFAULT_POOLER;
if (!NewKlass.poolSize) {
NewKlass.poolSize = DEFAULT_POOL_SIZE;
}
NewKlass.release = standardReleaser;
return NewKlass;
};
通过addPoolingTo
方法给传入的构造函数添加instancePool
属性和getPooled
,以及一个释放池的函数NewKlass.release = standardReleaser
,默认池大小为10。默认的getPooled
是带一个参数的池函数。PoolClass提供了很多个池函数,他们的区别就是参数的多少,默认是一个参数的。
// PoolClass.js
var oneArgumentPooler = function (copyFieldsFrom) {
var Klass = this;
if (Klass.instancePool.length) {
var instance = Klass.instancePool.pop();
Klass.call(instance, copyFieldsFrom);
return instance;
} else {
return new Klass(copyFieldsFrom);
}
};
这边可以看到如果池子里有的话,那么就直接弹出一个然后重新实例化,没有的话则重新创建。使用池来储存,减少内存消耗。
所以这边看到getPooled
方法那就是返回一个实例调用者的实例。
ReactUpdatesFlushTransaction
更新刷新事务,定义在ReactUpdates.js
中,在执行ReactUpdates.flushBatchedUpdates时开启。
// ReactUpdates.js
function ReactUpdatesFlushTransaction() {
this.reinitializeTransaction();
this.dirtyComponentsLength = null;
this.callbackQueue = CallbackQueue.getPooled();
this.reconcileTransaction = ReactUpdates.ReactReconcileTransaction.getPooled(true);
}
_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) {
// Essentially calls `this.reconcileTransaction.perform(method, scope, a)`
// with this transaction's wrappers around it.
return Transaction.perform.call(this, this.reconcileTransaction.perform, this.reconcileTransaction, method, scope, a);
}
});
PooledClass.addPoolingTo(ReactUpdatesFlushTransaction);
需要注意的是这边重写了perform
方法。并且使用PoolClass.addPoolingTo
添加了池属性。
外部包裹了两个wrapper
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.
// 额外的更新是由于 componentDidUpdate 或其他类似的操作。
// 在 update_queue wrapper close之前 我们希望运行这些新的更新以至于 如果 A 的componenDidUpdate 调用 B的 setState
// 那么 B 会在 A 的updater提供的回调之前更新
//
// 在我们自己的UPDATE_QUEUEING包装器关闭之前,我们希望运行这些新更新
// 以便如果A的componentDidUpdate在B上调用setState,那么B将在调用setState时提供回调A的updater之前更新。
dirtyComponents.splice(0, this.dirtyComponentsLength);
flushBatchedUpdates();
} else {
dirtyComponents.length = 0;
}
}
};
var UPDATE_QUEUEING = {
initialize: function () {
this.callbackQueue.reset();
},
close: function () {
this.callbackQueue.notifyAll();
}
};
var TRANSACTION_WRAPPERS = [NESTED_UPDATES, UPDATE_QUEUEING];
需要注意的是。在第一个wrapperNESTED_UPDATES
中的initialize
函数,初始化当前实例的dirtyComponentsLength = dirtyComponents.length
,之后在close
的时候,发现他们两长度不一样了,说明在执行method
时引起了额外的更新。这个时候就需要刷新这些新的更新。
而第二wrapperUPDATE_QUEUEING
在会close的时候执行callbackQueue.notifyAll
,这个方法是用来之前收集到的所有的回调。
这三个事务时目前我看到的三个,后面如果有其他的我会在加以分析。
总结
这一篇是介绍 React 中的Transaction, 以及目前三种自定义的事务内部的一些属性及包裹的wrapper