【Under-the-hood-ReactJS-Part10】React原始碼解讀

越山發表於2018-07-04

接上文,

React流程圖:
https://bogdan-lyashenko.gith…

‘髒’元件

從流程圖裡能看出,React會遍歷dirtyComponents陣列,並在事務中呼叫ReactUpdates.runBatchedUpdates。這個事務是個新事務。那麼為什麼要這麼設計呢?

此事務的型別為ReactUpdatesFlushTransaction,在此之前我們已經提到過,我們去看下其相應的包裝器以方便我們理解這個事務具體完成什麼任務。程式碼裡有如下注釋:

ReactUpdatesFlushTransaction包裝器會清空dirtyComponents陣列,並且執行所有已經壓入佇列裡的更新操作,這些操作一般都是由過程後的處理方法壓入的(比如,commponentDidUpdate方法)(ReactUpdatesFlushTransaction’s wrappers will clear the dirtyComponents array and perform any updates enqueued by mount-ready handlers (i.e., componentDidUpdate))

我們也驗證下是否程式碼真的這樣運轉。該事務有兩個包裝器,NEST_UPDATES和UPDATE_QUEUEING。在事務的初始化階段,我們會先把dirtyComponentsLength儲存起來,然後在關閉階段,React進行元件比較。在更新過程中,很可能dirtyComponents元件都發生裡改變,所以,需要不止一次的執行flushBatchedUpdates方法。程式碼比較直白,沒有什麼黑魔法。

但是,這裡其實有個地方需要注意下,ReactUpdatesFlushTransaction覆蓋了Transaction.perform方法,之所以這麼做,是因為,執行更新時需要呼叫ReactReconcileTransacation裡的行為(這個事務在掛載過程中也使用到了,目的是為了確保應用狀態安全)。所以,在ReactUpdatesFlushTransaction.perform方法的執行過程中,ReactReconcileTransaction方法會被呼叫到,所以,就是把事務方法在包了一次。

整個技術流程大概如此:

[NESTED_UPDATES, UPDATE_QUEUEING].initialize()
[SELECTION_RESTORATION, EVENT_SUPPRESSION, ON_DOM_READY_QUEUEING].initialize()

method -> ReactUpdates.runBatchedUpdates

[SELECTION_RESTORATION, EVENT_SUPPRESSION, ON_DOM_READY_QUEUEING].close()
[NESTED_UPDATES, UPDATE_QUEUEING].close()

在此文的最後,我們會回到事務方法,在確認下事務是如何幫助方法完成的,但在此之前,我們先確認下ReactUpdates.runBatchedUpdates的實現。(srcrendererssharedstackreconcilerReactUpdates.js#125)

在執行之前的第一步,就是把dirtyComponets進行排序,如何排序呢?基於mount order這個欄位進行排序(一個整數值,在元件例項化時被設定進元件),這個欄位代表父元件(它們也最先掛載)先更新,子元件接下去更新,以此類推。下一步,React會對updateBatchNumber加1操作,這個欄位類似於當前處理DOM的ID,看下程式碼裡的註釋,看下程式碼裡的註釋:

在更新過程中加入佇列的更新必須在批量操作執行完後再執行。否則,假設dirtyComponents為有元件A,B,其中A的子元件為B,B有子元件C,如果C有更新,則B將再次被壓入佇列,導致B被更新兩次。對於這種情況,我們只能通過檢查批量更新的計數器來跳過這次更新。(‘Any updates enqueued while reconciling must be performed after this entire batch. Otherwise, if dirtyComponents is [A, B] where A has children B and C, B could update twice in a single batch if C’s render enqueues an update to B (since B would have already updated, we should skip it, and the only way we can know to do so is by checking the batch counter).’)

這個設計避免了同一個元件的重複更新。

最後,我們遍歷完整個dirtyComponents佇列,然後把每個元件傳遞給了ReactReconciler.performUpdateIfNecessary,這個方法會在ReactCompisteCompoent裡被呼叫,所以,最終我們又回到了ReactCompsiteComponet裡的updateComponet方法。現在,我們可以更深入的研究下這個方法。

(未完待續)

相關文章