摘要:前言是一個十分龐大的庫,由于要同時考慮和,還有服務器渲染等,導致其代碼抽象化程度很高,嵌套層級非常深,閱讀其源碼是一個非常艱辛的過程。在學習源碼的過程中,給我幫助最大的就是這個系列文章,于是決定基于這個系列文章談一下自己的理解。
前言
React 是一個十分龐大的庫,由于要同時考慮 ReactDom 和 ReactNative ,還有服務器渲染等,導致其代碼抽象化程度很高,嵌套層級非常深,閱讀其源碼是一個非常艱辛的過程。在學習 React 源碼的過程中,給我幫助最大的就是這個系列文章,于是決定基于這個系列文章談一下自己的理解。本文會大量用到原文中的例子,想體會原汁原味的感覺,推薦閱讀原文。
本系列文章基于 React 15.4.2 ,以下是本系列其它文章的傳送門:
React 源碼深度解讀(一):首次 DOM 元素渲染 - Part 1
React 源碼深度解讀(二):首次 DOM 元素渲染 - Part 2
React 源碼深度解讀(三):首次 DOM 元素渲染 - Part 3
React 源碼深度解讀(四):首次自定義組件渲染 - Part 1
React 源碼深度解讀(五):首次自定義組件渲染 - Part 2
React 源碼深度解讀(六):依賴注入
React 源碼深度解讀(七):事務 - Part 1
React 源碼深度解讀(八):事務 - Part 2
React 源碼深度解讀(九):單個元素更新
React 源碼深度解讀(十):Diff 算法詳解
正文
上一篇文章介紹了 transaction 的基本概念和用法。今天我們將講解在更新過程中,React 是如何通過多個 transacion 之間的協作,來有效組織代碼的。
二、ReactUpdatesFlushTransaction
前文講到ReactDefaultBatchingStrategy close 的時候,會調用ReactUpdates.flushBatchedUpdates:
PooledClass.addPoolingTo(ReactUpdatesFlushTransaction); var flushBatchedUpdates = function () { while (dirtyComponents.length || asapEnqueued) { if (dirtyComponents.length) { var transaction = ReactUpdatesFlushTransaction.getPooled(); transaction.perform(runBatchedUpdates, null, transaction); ReactUpdatesFlushTransaction.release(transaction); } if (asapEnqueued) { asapEnqueued = false; var queue = asapCallbackQueue; asapCallbackQueue = CallbackQueue.getPooled(); queue.notifyAll(); CallbackQueue.release(queue); } } };
這里又調用了另一個 transaction 來處理后續的流程,有所不同的是 transaction 的創建不是直接 new,而是調用getPooled方法。這個方法是通過前面的PooledClass.addPoolingTo注入到ReactUpdatesFlushTransaction中的,如果對這個步驟感興趣可以看看這篇文章。下面來看ReactUpdatesFlushTransaction的內容:
Object.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) { return Transaction.perform.call( this, this.reconcileTransaction.perform, this.reconcileTransaction, method, scope, a ); }, } ); var TRANSACTION_WRAPPERS = [NESTED_UPDATES, UPDATE_QUEUEING]; 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. dirtyComponents.splice(0, this.dirtyComponentsLength); flushBatchedUpdates(); } else { dirtyComponents.length = 0; } }, }; var UPDATE_QUEUEING = { initialize: function () { this.callbackQueue.reset(); }, close: function () { this.callbackQueue.notifyAll(); }, };
ReactUpdatesFlushTransaction覆蓋了原型鏈上的perform方法,不是直接調用 callback,而是嵌套調用了this.reconcileTransaction.perform,在將 callback 透傳給reconcileTransaction的perform。這里的reconcileTransaction也開啟了實例池。
這里要注意下NESTED_UPDATES這個 wrapper,如果dirtyComponents的數量跟 transaction 開始的時候不一樣,它又會遞歸調用flushBatchedUpdates,直到dirtyComponents不再變化為止。UPDATE_QUEUEING這個 wrapper 暫時先忽略。
目前為止的調用關系如下:
三、ReactReconcileTransaction
ReactReconcileTransaction是一個普通的 transaction,定義了一些 DOM 操作相關的 wrapper:
function ReactReconcileTransaction(useCreateElement: boolean) { this.reinitializeTransaction(); this.renderToStaticMarkup = false; this.reactMountReady = CallbackQueue.getPooled(null); this.useCreateElement = useCreateElement; } var Mixin = { getTransactionWrappers: function() { return TRANSACTION_WRAPPERS; }, ... destructor: function() { CallbackQueue.release(this.reactMountReady); this.reactMountReady = null; }, }; Object.assign(ReactReconcileTransaction.prototype, Transaction, Mixin); PooledClass.addPoolingTo(ReactReconcileTransaction); var TRANSACTION_WRAPPERS = [ SELECTION_RESTORATION, EVENT_SUPPRESSION, ON_DOM_READY_QUEUEING, ]; 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(); }, };
這三個 wrapper 的作用注釋都講得很清楚了,不再贅述。值得一提的是這里perform的 callback 是ReactUpdatesFlushTransaction透傳過來的ReactUpdate.runBatchedUpdates。
目前為止的調用關系如下:
四、總結
到此為止,transaction 相關的內容就講完了,下一篇開始介紹真正的更新操作。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/99092.html
摘要:在學習源碼的過程中,給我幫助最大的就是這個系列文章,于是決定基于這個系列文章談一下自己的理解。到此為止,首次渲染就完成啦總結從啟動到元素渲染到頁面,并不像看起來這么簡單,中間經歷了復雜的層級調用。 前言 React 是一個十分龐大的庫,由于要同時考慮 ReactDom 和 ReactNative ,還有服務器渲染等,導致其代碼抽象化程度很高,嵌套層級非常深,閱讀其源碼是一個非常艱辛的過...
摘要:本篇開始介紹自定義組件是如何渲染的。組件將自定義組件命名為,結構如下經過編譯后,生成如下代碼構建頂層包裝組件跟普通元素渲染一樣,第一步先會執行創建為的。調用順序已在代碼中注釋。先看圖,這部分內容將在下回分解 前言 React 是一個十分龐大的庫,由于要同時考慮 ReactDom 和 ReactNative ,還有服務器渲染等,導致其代碼抽象化程度很高,嵌套層級非常深,閱讀其源碼是一個非...
摘要:依賴注入和控制反轉,這兩個詞經常一起出現。一句話表述他們之間的關系依賴注入是控制反轉的一種實現方式。而兩者有大量的代碼都是可以共享的,這就是依賴注入的使用場景了。下一步就是創建具體的依賴內容,然后注入到需要的地方這里的等于這個對象。 前言 React 是一個十分龐大的庫,由于要同時考慮 ReactDom 和 ReactNative ,還有服務器渲染等,導致其代碼抽象化程度很高,嵌套層級...
摘要:在學習源碼的過程中,給我幫助最大的就是這個系列文章,于是決定基于這個系列文章談一下自己的理解。說明就算拋出了錯誤,部分的代碼也要繼續執行,隨后再將錯誤往上層代碼拋。和都能保證其中一個成員拋出錯誤的時候,余下的能繼續執行。 前言 React 是一個十分龐大的庫,由于要同時考慮 ReactDom 和 ReactNative ,還有服務器渲染等,導致其代碼抽象化程度很高,嵌套層級非常深,閱讀...
摘要:本文將要講解的調用棧是下面這個樣子的平臺無關相關如果看源碼,我們會留意到很多相關的代碼,我們暫時先將其忽略,會在后續的文章中進行講解?,F在我們來看一下各實例間的關系目前為止的調用棧平臺無關相關下一篇講解三總結本文講解了轉化為的過程。 歡迎關注我的公眾號睿Talk,獲取我最新的文章:showImg(https://segmentfault.com/img/bVbmYjo); 一、前言 R...
閱讀 1823·2021-09-28 09:46
閱讀 3143·2019-08-30 14:22
閱讀 1878·2019-08-26 13:36
閱讀 3343·2019-08-26 11:32
閱讀 2080·2019-08-23 16:56
閱讀 1151·2019-08-23 16:09
閱讀 1303·2019-08-23 12:55
閱讀 2148·2019-08-23 11:44