对react有一定了解的开发人员应该都听说过react的事务机制,这个机制几乎贯穿于react所有提供的方法,包括react中最常使用的setState函数

那么,react的事务机制到底是一种什么样的机制呢,为了解释事务机制的实现原理,react源码中用注释画出了这样一幅图




根据这幅图,我们可以这样理解事务,react中用事务执行方法,就是用wrapper(称之为套套吧)把方法包裹起来,然后每个wapper中都提供一个initialize方法和一个close方法,当需要使用事务调用一个方法,例如上图中的anyMethod时,使用事务提供的perform方法,将需要执行的方法传入,这个时候就会按顺序执行wrapper.initalize,anyMethod,wrapper.close,而且,事务还支持多个事务的嵌套,当执行方法被多个wapper包裹时,事务会先按顺序执行所有的initalize方法,再执行anyMethod,最后按顺序执行所有的close函数,例如上图就表示会按以下顺序执行wrapper1.initalize,wrapper2.initalize,anyMethod,wrapper1.close,wrapper2.close

那么事务在react中到底是怎么应用的呢?我们透过下面这段代码来看看如何使用事务
var ReactUpdates = require('ReactUpdates'); var Transaction =
require('Transaction'); var emptyFunction = require('emptyFunction');
//第二个wrapper var RESET_BATCHED_UPDATES = { initialize: emptyFunction, close:
function() { ReactDefaultBatchingStrategy.isBatchingUpdates = false; }, };
//第一个wrapper var FLUSH_BATCHED_UPDATES = { initialize: emptyFunction, close:
ReactUpdates.flushBatchedUpdates.bind(ReactUpdates), }; //wrapper列表 var
TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES]; //事务构造函数
function ReactDefaultBatchingStrategyTransaction() { //原型中定义的初始化方法
this.reinitializeTransaction(); } //继承原型 Object.assign(
ReactDefaultBatchingStrategyTransaction.prototype, Transaction.Mixin, {
getTransactionWrappers: function() { return TRANSACTION_WRAPPERS; }, } );
//新建一个事务 var transaction = new ReactDefaultBatchingStrategyTransaction(); 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) { callback(a,
b, c, d, e); } else {
//在这个地方调用事务,callback是从外部传入的方法,此处无需关心callback,只需要知道它是一个待执行方法即可
transaction.perform(callback, null, a, b, c, d, e); } }, };

代码不长,但确是react中用来控制渲染逻辑最关键的代码,此处只是为了展示事务的使用方法,我们无需关心代码做了些什么,我们可以看到上面的代码定义了一个叫ReactDefaultBatchingStrategyTransaction的构造函数,这个其实就是我们自定义的一个事务,并且在构造函数的原型上实现了一个叫getTransactionWrappers的接口,代码中的这个接口返回了一个数组,而这个数组的每一个项都包含了initalize和close方法,当调用perform方法时,事务会依次去调用这两个方法,而我们的看到的perform方法和构造函数中的reinitializeTransaction方法都是在Transaction.Mixin中定义的,这里面提供了实现事务所需要的所有基础功能,我们可以来看一下源代码的实现逻辑
'use strict'; var invariant = require('invariant'); var Mixin = { //事务初始化函数
reinitializeTransaction: function() { this.transactionWrappers =
this.getTransactionWrappers(); if (this.wrapperInitData) {
this.wrapperInitData.length = 0; } else { this.wrapperInitData = []; }
this._isInTransaction = false; }, _isInTransaction: false,
//抽象接口,此处的getTransactionWrappers方法将会被外部定义的getTransactionWrappers方法所覆盖
getTransactionWrappers: null, isInTransaction: function() { return
!!this._isInTransaction; }, perform: function(method, scope, a, b, c, d, e, f)
{ var errorThrown; var ret; try { this._isInTransaction = true; errorThrown =
true; //执行所有的initalize方法 this.initializeAll(0); //执行真正的方法 ret =
method.call(scope, a, b, c, d, e, f); errorThrown = false; } finally { try { if
(errorThrown) { try { this.closeAll(0); } catch (err) { } } else {
//正常状态下执行所有close方法 this.closeAll(0); } } finally { this._isInTransaction =
false; } } return ret; }, //执行所有wrapper中的initialize函数 initializeAll:
function(startIndex) { var transactionWrappers = this.transactionWrappers; for
(var i = startIndex; i < transactionWrappers.length; i++) { var wrapper =
transactionWrappers[i]; try {
//定义一个初始值,当finally里面匹配到this.wrapperInitData[i]未改变时,会忽略此项,从下一个项开始重新执行一次初始化,这么做的原因。。。官方解释为用catch使调试变的复杂,开心就好
this.wrapperInitData[i] = Transaction.OBSERVED_ERROR; //执行initialize方法
this.wrapperInitData[i] = wrapper.initialize ? wrapper.initialize.call(this) :
null; } finally { if (this.wrapperInitData[i] === Transaction.OBSERVED_ERROR) {
try { this.initializeAll(i + 1); } catch (err) { } } } } },
//执行所有wrapper中的close函数 closeAll: function(startIndex) { invariant(
this.isInTransaction(), 'Transaction.closeAll(): Cannot close transaction when
none are open.' ); var transactionWrappers = this.transactionWrappers; for (var
i = startIndex; i < transactionWrappers.length; i++) { var wrapper =
transactionWrappers[i]; var initData = this.wrapperInitData[i]; var
errorThrown; try { //标记是否有执行异常,这么做的原因。。。官方解释为用catch使调试变的复杂,开心就好 errorThrown =
true; //执行close方法 if (initData !== Transaction.OBSERVED_ERROR && wrapper.close)
{ wrapper.close.call(this, initData); } errorThrown = false; } finally {
//存在异常异常 if (errorThrown) { try { this.closeAll(i + 1); } catch (e) { } } } }
this.wrapperInitData.length = 0; }, }; var Transaction = { Mixin: Mixin,
OBSERVED_ERROR: {}, }; module.exports = Transaction;

这就是整个事务机制的实现代码,没有删减,逻辑非常清晰,也很简单,这里其实就是使用了原型模式,这个就是所有事务的原型,前面我们看到在自定义事务里面使用的perform方法和reinitializeTransaction方法都可以在这里面找到,其实核心原理就是定义一个原型,里面有一个用来存放wrappers(称之为套套吧)的抽象接口,会由继承于这个原型的子类覆盖,由子类来定义真正要执行的方法前后执行的所有东西,我们可以看到perform方法的逻辑其实也很清晰,就是先调用initializeAll方法执行所有的initialize方法,然后执行传入的主体方法,之后执行closeAll来执行所有的close方法,所以,看起来,目测也不是什么高深的东西,也就是在执行方法之前强行添加入口跟出口方法,代码比较简单,都加了注释,感兴趣的话可以自行了解一下


这里面有个比较骚的操作在于,不管在initializeAll还是closeAll中,都是使用一个临时变量来标记当前的执行过程是否有异常,舍弃了用catch方法,改用在finally方法里面处理异常,官方的说法是捕获错误会使调试变的困难,具体原因应该是因为作为基础的原型类,
此处不应该去捕获外部传入方法抛出的异常,应该让异常直接抛回外部,由外部函数自行处理,如果此处强行将异常捕获,确实会导致外部函数调试变的比较困难

友情链接
KaDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:ixiaoyang8@qq.com
QQ群:637538335
关注微信