【譯】了解React源代碼-UI更新(事務(wù))7

【譯】了解React源代碼-初始渲染(簡單組件)1
【譯】了解React源代碼-初始渲染(簡單組件)2
【譯】了解React源代碼-初始渲染(簡單組件)3
【譯】了解React源代碼-初始渲染(類組件)4
【譯】了解React源代碼-初始渲染(類組件)5
【譯】了解React源代碼-UI更新(事務(wù))6
【譯】了解React源代碼-UI更新(事務(wù))7
【譯】了解React源代碼-UI更新(單個DOM)8
【譯】了解React源代碼-UI更新(DOM樹)9


上次我們討論了核心的Transaction類和ReactDefaultBatchingStrategyTransaction。 實例化不是UI更新中涉及的唯一Transaction,而僅僅是領(lǐng)導(dǎo)者。

在本文中,我們將研究其他Transaction,以及ReactDefaultBatchingStrategyTransaction概述UI更新處理邏輯。

本文中使用的文件:

renderers / shared / stack / reconciler / ReactUpdates.js
定義flushBatchedUpdates()是本文的切入點方法; 它還定義了ReactUpdatesFlushTransaction,這是我們將要討論的Transaction之一

shared / utils / PooledClass.js
定義PooledClass以啟用實例池

renderers / dom / client / ReactReconcileTransaction.js
定義ReactReconcileTransaction,這是我們要討論的另一個Transaction

We start with the ReactUpdates.flushBatchedUpdates() {last post *8}.
我們從ReactUpdates.flushBatchedUpdates(){上一篇 * 8}開始。

...
var flushBatchedUpdates = function () {
  while (dirtyComponents.length || asapEnqueued) {
    if (dirtyComponents.length) {
      var transaction = ReactUpdatesFlushTransaction.getPooled();
      transaction.perform(runBatchedUpdates, null, transaction);
      ReactUpdatesFlushTransaction.release(transaction);
    }
    if (asapEnqueued) { // scr: not applied
...
    }
  }
};
...

ReactUpdates@renderers/shared/stack/reconciler/ReactUpdates.js

如前所述,它通過啟動一個ReactUpdatesFlushTransaction來處理所有臟組件,該ReactUpdatesFlushTransaction最終將調(diào)用runBatchedUpdates

PooledClass-維護(hù)實例池

這兩個不常見的方法getPooled()release()繼承自PooledClass,后者提供了實例池化能力:

a)如果池中沒有分配的實例(在本例中為ReactUpdatesFlushTransaction),則getPooled()創(chuàng)建一個新實例;

b)如果池中存在實例,則getPooled()僅返回該實例;

c)release()不會釋放實例,相反,它只是將實例放回池中。

像任何其他類型的池一樣,此實例池的最終目的是減少多余資源(在這種情況下為內(nèi)存)分配和銷毀的開銷。

回到上面的具體情況:

...
  while (dirtyComponents.length || asapEnqueued) {
    if (dirtyComponents.length) {
      var transaction = ReactUpdatesFlushTransaction.getPooled();
...

僅在第一次執(zhí)行while循環(huán)時才分配ReactUpdatesFlushTransaction實例。 之后,可以直接從池中通過getPooled()獲得實例。

This level of understanding of PooledClass is sufficient for the rest of this post. So feel free to fast travel to the the next section by ctrl-f “ReactUpdatesFlushTransaction”.
PooledClass的這種理解水平足以滿足本文的其余部分。 因此,可以通過ctrl -f“ ReactUpdatesFlushTransaction”快速進(jìn)入下一部分。

現(xiàn)在我們來看一下它的實現(xiàn):

var PooledClass = {
  addPoolingTo: addPoolingTo,                     // scr: ------> 1)
  oneArgumentPooler: (oneArgumentPooler: Pooler), // scr: ------> 2)
...// scr: not used
};

module.exports = PooledClass;

PooledClass@shared/utils/PooledClass.js

1)addPoolingTo()是一種“公共”方法,它將池功能添加到一個類中;

2)oneArgumentPooler()getPooled()的基礎(chǔ)實現(xiàn)。

接下來,我們看一下addPoolingTo()的函數(shù)體:

...
var DEFAULT_POOL_SIZE = 10;
var DEFAULT_POOLER = oneArgumentPooler;
...

var addPoolingTo = function<T>(
  CopyConstructor: Class<T>,
  pooler: Pooler,
): Class<T> & {
  getPooled(): /* arguments of the constructor */ T,
  release(): void,
} {
  var NewKlass = (CopyConstructor: any);
  NewKlass.instancePool = [];              // scr: -------------> 1)
  NewKlass.getPooled = pooler || DEFAULT_POOLER; // scr: -------> 2)
  if (!NewKlass.poolSize) {
    NewKlass.poolSize = DEFAULT_POOL_SIZE; // scr: -------------> 3)
  }
  NewKlass.release = standardReleaser;     // scr: -------------> 4)
  return NewKlass;
};
...

addPoolingTo@shared/utils/PooledClass.js

1)instancePool是池;

2)將DEFAULT_POOLER(也稱為oneArgumentPooler)附加到getPooled();

3)將poolSize設(shè)置為10;

4)將standardReleaser()附加到release()。

這就是getPooled()release()的實現(xiàn)方式:

var oneArgumentPooler = function(copyFieldsFrom) {
  var Klass = this;
  if (Klass.instancePool.length) {           // scr: -----------> 1)
    var instance = Klass.instancePool.pop(); // scr: -----------> 1)
    Klass.call(instance, copyFieldsFrom);
    return instance;
  } else {
    return new Klass(copyFieldsFrom);   // scr: ----------------> 2)
  }
};

oneArgumentPooler@shared/utils/PooledClass.js

...

var standardReleaser = function(instance) {
  var Klass = this;
...
  instance.destructor();
  if (Klass.instancePool.length < Klass.poolSize) { // scr: ----> 3)
    Klass.instancePool.push(instance);              // scr: ----> 3)
  }
};

standardReleaser@shared/utils/PooledClass.js

1)對應(yīng)于b),其中

Klass.call(instance, copyFieldsFrom);

使用指定的參數(shù)(copyFieldsFrom)調(diào)用Klass構(gòu)造函數(shù)以初始化啟用池的類; 和

2)對應(yīng)于a); 和

3)對應(yīng)于c)。

最后,我們從外部看如何使用addPoolingTo()ReactUpdatesFlushTransaction):

...
PooledClass.addPoolingTo(ReactUpdatesFlushTransaction);
...

ReactUpdatesFlushTransaction@renderers/shared/stack/reconciler/ReactUpdates.js

ReactUpdatesFlushTransaction

...
function ReactUpdatesFlushTransaction() {
  this.reinitializeTransaction();
  this.dirtyComponentsLength = null;
  this.callbackQueue = CallbackQueue.getPooled();
  this.reconcileTransaction =
  ReactUpdates.ReactReconcileTransaction.getPooled(  // scr: ---> 2)
    /* useCreateElement */ true,
  );
}

// scr: --------------------------------------------------------> 1)
Object.assign(ReactUpdatesFlushTransaction.prototype, Transaction, {
  getTransactionWrappers: function() {
    return TRANSACTION_WRAPPERS; // scr: -----------------------> 3)
  },

  destructor: function() {
    this.dirtyComponentsLength = null;
    CallbackQueue.release(this.callbackQueue);
    this.callbackQueue = null;
    ReactUpdates.ReactReconcileTransaction.release( // scr: ----> 2)
      this.reconcileTransaction
    );

    this.reconcileTransaction = null;
  },

  perform: function(method, scope, a) {
    return Transaction.perform.call(
      this,
      this.reconcileTransaction.perform, // scr: ---------------> 2)
      this.reconcileTransaction,
      method,
      scope,
      a,
    );
  },
});
...

ReactUpdatesFlushTransaction@renderers/shared/stack/reconciler/ReactUpdates.js

1)是Transaction的另一個實例化,它覆蓋了perform()方法;

2)取代直接調(diào)用ReactUpdate.runBatchedUpdates(回調(diào))的方法,重寫的ReactUpdatesFlushTransaction.perform()嵌套調(diào)用了另一個TransactionReactReconcileTransaction)的perform()方法和傳遞方法(即ReactUpdate.runBatchedUpdates())作為其回調(diào)方法。 請注意,ReactReconcileTransaction也已啟用池。

3)TRANSACTION_WRAPPERS定義其前置和后置功能:

...
var NESTED_UPDATES = {
  initialize: function() {
    this.dirtyComponentsLength = dirtyComponents.length;
  },
  close: function() {
    if (this.dirtyComponentsLength !== dirtyComponents.length) {
      dirtyComponents.splice(0, this.dirtyComponentsLength);
      flushBatchedUpdates();      // scr: ----------------------> a)
    } else {
      dirtyComponents.length = 0; // scr: ----------------------> b)
    }
  },
};

var UPDATE_QUEUEING = { // scr: ------> we omit this wrapper for now
  initialize: function() {
    this.callbackQueue.reset();
  },
  close: function() {
    this.callbackQueue.notifyAll();
  },
};

var TRANSACTION_WRAPPERS = [NESTED_UPDATES, UPDATE_QUEUEING];
...

ReactUpdatesFlushTransaction@renderers/shared/stack/reconciler/ReactUpdates.js

NESTED_UPDATES的initialize()1.5)存儲dirtyComponents的數(shù)量; 其close()3)檢查數(shù)字是否已更改。 如果它們不同{a},則調(diào)用flushBatchedUpdates()來重復(fù)該過程; 或會將dirtyComponents.length設(shè)置回0,然后返回上一級Transaction ReactDefaultBatchingStrategyTransaction {上一篇}。

I will not examine the CallbackQueue related operations (in UPDATE_QUEUEING) and will leave them for later articles when discussing component’s life cycle. *9
我將不討論與CallbackQueue相關(guān)的操作(在UPDATE_QUEUEING中),并將在討論組件的生命周期時將其留給以后的文章。 * 9

回顧一下:

ReactReconcileTransaction

這是另一個Transaction,沒有異常。

var Mixin = {
  /**
   * @see Transaction
   * @abstract
   * @final
   * @return {array<object>} List of operation wrap procedures.
   *   TODO: convert to array<TransactionWrapper>
   */
  getTransactionWrappers: function() {
    return TRANSACTION_WRAPPERS; // scr: -----------------------> 1)
  },
  // scr: ---------------------> we omit all CallbackQueue s for now
  getReactMountReady: function() {
    return this.reactMountReady;
  },

  // scr: ---------------------> we omit all CallbackQueue s for now
  getUpdateQueue: function() {
    return ReactUpdateQueue;
  },

  checkpoint: function() { // scr: -----------------------> not used
    // reactMountReady is the our only stateful wrapper
    return this.reactMountReady.checkpoint();
  },

  rollback: function(checkpoint) { // scr: ---------------> not used
    this.reactMountReady.rollback(checkpoint);
  },

  // scr: ------------------------------------> for instance pooling
  destructor: function() {
    CallbackQueue.release(this.reactMountReady);
    this.reactMountReady = null;
  },
};

Object.assign(ReactReconcileTransaction.prototype, Transaction, Mixin);

// scr: --------------------------------------------------------> 2)
PooledClass.addPoolingTo(ReactReconcileTransaction);

module.exports = ReactReconcileTransaction;

ReactReconcileTransaction@renderers/dom/client/ReactReconcileTransaction.js

1)它的包裝器在TRANSACTION_WRAPPERS中定義;

2)如前所述,此類已啟用池。

接下來我們看一下它的三個包裝:

/**
 * Ensures that, when possible, the selection range (currently selected text
 * input) is not disturbed by performing the transaction.
 */
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();
  },
};

var TRANSACTION_WRAPPERS = [
  SELECTION_RESTORATION,
  EVENT_SUPPRESSION,
  ON_DOM_READY_QUEUEING,
];

ReactReconcileTransaction@renderers/dom/client/ReactReconcileTransaction.js

這里的評論很清楚:

SELECTION_RESTORATION用于在UI更新之前存儲文本字段的焦點狀態(tài)(initialize()),并在(close())之后恢復(fù)狀態(tài)。

EVENT_SUPPRESSION用于存儲啟用事件的切換狀態(tài),并在(initialize())之前臨時禁用事件,并在UI更新(close())之后恢復(fù)狀態(tài)。

Again, I will not examine the CallbackQueue related operations (in ON_DOM_READY_QUEUEING) here and will leave them for later articles when discussing component’s life cycle. *10
同樣,我將不在這里檢查與CallbackQueue相關(guān)的操作(在ON_DOM_READY_QUEUEING中),并將在討論組件的生命周期時將其留給以后的文章。 * 10

重要的是要注意ReactReconcileTransaction依賴于默認(rèn)的Transaction.perform()。 它的回調(diào)是ReactUpdate.runBatchedUpdates,它一直向下傳遞到該級別。

ctrl-f “runBatchedUpdates” to examine its route.
ctrl -f“ runBatchedUpdates”檢查其路由。

這個ReactUpdate.runBatchedUpdates將導(dǎo)致我的下一篇文章的內(nèi)容。

回顧一下:

結(jié)論

(原文鏈接)Understanding The React Source Code - UI Updating (Transactions) VII

(上一篇)【譯】了解React源代碼-UI更新(事務(wù))6

(下一篇)【譯】了解React源代碼-UI更新(單個DOM)8

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容