本文使用的是react 15.6.1的代碼
上篇介紹batchedUpdates時,最后flushBatchedUpdates方法中使用了Pool和CallbackQueus中的方法,這次我們就詳細(xì)介紹一下這兩個js。
Pool
承接上文,先來看看當(dāng)時是怎么使用的。
var flushBatchedUpdates = function() {
while (dirtyComponents.length || asapEnqueued) {
if (dirtyComponents.length) {
// 從緩存池中獲取ReactUpdatesFlushTransaction對象
var transaction = ReactUpdatesFlushTransaction.getPooled();
// 調(diào)用runBatchedUpdates
transaction.perform(runBatchedUpdates, null, transaction);
ReactUpdatesFlushTransaction.release(transaction);
}
if (asapEnqueued) {
asapEnqueued = false;
var queue = asapCallbackQueue;
asapCallbackQueue = CallbackQueue.getPooled();
queue.notifyAll();
CallbackQueue.release(queue);
}
}
};
在這里,首先發(fā)現(xiàn),這里的transaction和 前文ReactDefaultBatchingStrategyTransaction不同,前文是通過new 的方式實例化的對象,而這里是調(diào)用ReactUpdatesFlushTransaction.getPooled()獲取,朔本清源,看看ReactUpdatesFlushTransaction和ReactDefaultBatchingStrategyTransaction具體有什么區(qū)別
var NESTED_UPDATES = {
initialize: function() {
this.dirtyComponentsLength = dirtyComponents.length;
},
close: function() {
// 在批量更新,如果有新的dirtyComponents被push,那么,需要再一次批量更新,從新加入的dirtyComponents開始
if (this.dirtyComponentsLength !== dirtyComponents.length) {
dirtyComponents.splice(0, this.dirtyComponentsLength);
flushBatchedUpdates();
} else {
dirtyComponents.length = 0;
}
},
};
var UPDATE_QUEUEING = {
initialize: function() {
// 重置回調(diào)隊列
this.callbackQueue.reset();
},
close: function() {
// 執(zhí)行回調(diào)方法
this.callbackQueue.notifyAll();
},
};
var TRANSACTION_WRAPPERS = [NESTED_UPDATES, UPDATE_QUEUEING];
// ReactUpdatesFlushTransaction構(gòu)造函數(shù)
function ReactUpdatesFlushTransaction() {
// 調(diào)用reinitializeTransaction
this.reinitializeTransaction();
this.dirtyComponentsLength = null;
//獲取callbackQueue,reconcileTransaction實例;
this.callbackQueue = CallbackQueue.getPooled();
this.reconcileTransaction = ReactUpdates.ReactReconcileTransaction.getPooled(
/* useCreateElement */ true,
);
}
// 繼承,覆蓋相關(guān)方法
Object.assign(ReactUpdatesFlushTransaction.prototype, Transaction, {
// 覆蓋getTransactionWrappers方法
getTransactionWrappers: function() {
return TRANSACTION_WRAPPERS;
},
// 覆蓋destructor方法,該方法會在放回緩存池中調(diào)用
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);
和普通的transation一樣,似乎并沒有什么不同,都有對應(yīng)的wrappers getTransactionWrappers,對應(yīng)構(gòu)造函數(shù),以及都重寫了getTransactionWrappers方法。但是細(xì)細(xì)發(fā)現(xiàn)后,發(fā)現(xiàn)這個transaction重寫了destructor方法,同時執(zhí)行了命令PooledClass.addPoolingTo(ReactUpdatesFlushTransaction);,那么,做這些是有什么作用呢?這里就引出了React的一個類庫PooledClass,來看看他的實現(xiàn)吧!
var oneArgumentPooler = function(copyFieldsFrom) {
var Klass = this;
// 如果緩存池長度不為0,即存在實體對象,
if (Klass.instancePool.length) {
//那么直接從緩存池返回對應(yīng)的對象
var instance = Klass.instancePool.pop();
// 同時調(diào)用一次該類構(gòu)造函數(shù),對該instance進行初始化
Klass.call(instance, copyFieldsFrom);
return instance;
} else {
// 如果沒有緩存,則new一個出來
return new Klass(copyFieldsFrom);
}
}
var standardReleaser = function(instance) {
var Klass = this;
// 調(diào)用實例的destructor方法
instance.destructor();
// 將實例壓入池子
if (Klass.instancePool.length < Klass.poolSize) {
Klass.instancePool.push(instance);
}
};
var DEFAULT_POOL_SIZE = 10;
var DEFAULT_POOLER = oneArgumentPooler;
type Pooler = any;
//重點代碼
var addPoolingTo = function<T>(
CopyConstructor: Class<T>,
pooler: Pooler,
): Class<T> & {
getPooled(): /* arguments of the constructor */ T,
release(): void,
} {
// Casting as any so that flow ignores the actual implementation and trusts
// it to match the type we declared
var NewKlass = (CopyConstructor: any);
// 該類的具體實例緩存池
NewKlass.instancePool = [];
NewKlass.getPooled = pooler || DEFAULT_POOLER;
if (!NewKlass.poolSize) {
NewKlass.poolSize = DEFAULT_POOL_SIZE;
}
NewKlass.release = standardReleaser;
return NewKlass;
};
var PooledClass = {
addPoolingTo: addPoolingTo,
oneArgumentPooler: (oneArgumentPooler: Pooler),
twoArgumentPooler: (twoArgumentPooler: Pooler),
threeArgumentPooler: (threeArgumentPooler: Pooler),
fourArgumentPooler: (fourArgumentPooler: Pooler),
};
代碼很簡單,主要是提供了 addPoolingTo這個方法,在頁面調(diào)用該方法后,會將getPooled以及release方法以及instancePool掛載到對應(yīng)的類上,當(dāng)調(diào)用getPooled方法后,會優(yōu)先從instancePool去尋找是否有已經(jīng)生成的實例,如果有,將其初始化(執(zhí)行class的構(gòu)造函數(shù)),沒有的話,則new一個出來。當(dāng)對象使用完畢后,調(diào)用一下release方法,這時,會調(diào)用實例的destructor方法去銷毀實例中的對象等數(shù)據(jù),同時放入緩存池中等待下一次調(diào)用,為什么react中要用這樣的方法而不是直接new呢。因為new 一個function的時候,其會在原型鏈去查找屬性(比較耗時),
詳細(xì)見 繼承與原型鏈
CallbackQueue
CallbackQueue代碼比較簡單,我們直接看看實現(xiàn)吧
class CallbackQueue<T> {
// 回調(diào)隊列
_callbacks: ?Array<() => void>;
//上下文
_contexts: ?Array<T>;
_arg: ?mixed;
constructor(arg) {
this._callbacks = null;
this._contexts = null;
this._arg = arg;
}
/**
* Enqueues a callback to be invoked when `notifyAll` is invoked.
*
* @param {function} callback Invoked when `notifyAll` is invoked.
* @param {?object} context Context to call `callback` with.
* @internal
*/
enqueue(callback: () => void, context: T) {
//將回調(diào)和上下文塞入隊列中
this._callbacks = this._callbacks || [];
this._callbacks.push(callback);
this._contexts = this._contexts || [];
this._contexts.push(context);
}
/**
* 執(zhí)行隊列中所有回調(diào)函數(shù)
*
* @internal
*/
notifyAll
() {
var callbacks = this._callbacks;
var contexts = this._contexts;
var arg = this._arg;
if (callbacks && contexts) {
this._callbacks = null;
this._contexts = null;
for (var i = 0; i < callbacks.length; i++) {
callbacks[i].call(contexts[i], arg);
}
callbacks.length = 0;
contexts.length = 0;
}
}
/**
* 檢查點,返回隊列長度
* @returns {number}
*/
checkpoint() {
return this._callbacks ? this._callbacks.length : 0;
}
/**
*
* @param len
*/
rollback(len: number) {
if (this._callbacks && this._contexts) {
this._callbacks.length = len;
this._contexts.length = len;
}
}
/**
* 重置隊列以及上下文
*
* @internal
*/
reset() {
this._callbacks = null;
this._contexts = null;
}
/**
* Pool調(diào)用release后會執(zhí)行
*/
destructor() {
this.reset();
}
}
module.exports = PooledClass.addPoolingTo(CallbackQueue);
代碼比較簡單,用flow來編寫。不過從設(shè)計思路上可以發(fā)現(xiàn),在react中,往往是把上下文以及組件回調(diào)先存入這個統(tǒng)一的隊列中,最后執(zhí)行notifyAll來順序執(zhí)行回調(diào)。同時隊列中提供了這樣幾個API供外部調(diào)用分別是
- enqueue : 將回調(diào)和上下文塞入隊列的方法
- notifyAll: 通知調(diào)用回調(diào)隊列的方法
- checkpoint: 檢查點,返回隊列長度
- rollback: 設(shè)置隊列長度為指定數(shù)字
- reset: 重置隊列以及上下文