摘要:本文將要講解的調用棧是下面這個樣子的平臺無關相關如果看源碼,我們會留意到很多相關的代碼,我們暫時先將其忽略,會在后續的文章中進行講解。現在我們來看一下各實例間的關系目前為止的調用棧平臺無關相關下一篇講解三總結本文講解了轉化為的過程。
歡迎關注我的公眾號睿Talk,獲取我最新的文章:
React 是一個十分龐大的庫,由于要同時考慮 ReactDom 和 ReactNative ,還有服務器渲染等,導致其代碼抽象化程度很高,嵌套層級非常深。閱讀 React 源碼是一個非常艱辛的過程,在學習過程中給我幫助最大的就是這個系列文章。作者對代碼的調用關系梳理得非常清楚,而且還有配圖幫助理解,非常值得一讀。站在巨人的肩膀之上,我嘗試再加入自己的理解,希望對有志于學習 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 算法詳解
上一篇文章中,介紹了頂層對象ReactCompositeComponent[T]是如何構造的,接下來我們看看 batchedMountComponentIntoNode 做了什么事情。
本文將要講解的調用棧是下面這個樣子的:
|=ReactMount.render(nextElement, container, callback) ___ |=ReactMount._renderSubtreeIntoContainer() | |-ReactMount._renderNewRootComponent() | |-instantiateReactComponent() | |~batchedMountComponentIntoNode() upper half |~mountComponentIntoNode() (平臺無關) |-ReactReconciler.mountComponent() | |-ReactCompositeComponent.mountComponent() | |-ReactCompositeComponent.performInitialMount() | |-instantiateReactComponent() _|_ |-ReactDOMComponent.mountComponent() lower half |-_mountImageIntoNode() (HTML DOM 相關) _|_
如果看源碼,我們會留意到很多transaction相關的代碼,我們暫時先將其忽略,會在后續的文章中進行講解。暫時可以理解為調用transaction.perform時,實際上就是對第一個參數進行函數調用。跳過一些模版代碼后,實際上做事情的是 mountComponentIntoNode 這個方法。
// 文件位置:src/renderers/dom/client/ReactMount.js function mountComponentIntoNode( wrapperInstance, // ReactCompositeComponent[T] container, // document.getElementById("root") transaction, shouldReuseMarkup, context ) { ... var markup = ReactReconciler.mountComponent( wrapperInstance, transaction, null, ReactDOMContainerInfo(wrapperInstance, container), context, 0 /* parentDebugID */ ); ... ReactMount._mountImageIntoNode( markup, container, wrapperInstance, shouldReuseMarkup, transaction ); }
ReactReconciler.mountComponent 用于創建 DOM 元素,而 ReactMount._mountImageIntoNode 則是將剛創建的 DOM 元素掛載到頁面。ReactReconciler.mountComponent 會調用 ReactCompositeComponent[T]的 mountComponent 方法。在看 mountComponent 方法前,還需要先準備好 hostContainerInfo,由 ReactDOMContainerInfo 生成:
// 文件位置:src/renderers/dom/shared/ReactDOMContainerInfo.js function ReactDOMContainerInfo( topLevelWrapper, // ReactCompositeComponent[T] node // document.getElementById("root") ) { var info = { _topLevelWrapper: topLevelWrapper, _idCounter: 1, _ownerDocument: node ? node.nodeType === DOC_NODE_TYPE ? node : node.ownerDocument : null, _node: node, _tag: node ? node.nodeName.toLowerCase() : null, _namespaceURI: node ? node.namespaceURI : null, }; ... return info; }
現在各實例間的關系是這樣的:
再繼續看 mountComponent 方法:
// 文件位置:src/renderers/shared/stack/reconciler/ReactCompositeComponent.js mountComponent: function ( transaction, hostParent, hostContainerInfo, context ) { ... // this._currentElement 為ReactElement[2](TopLevelWrapper) var publicProps = this._currentElement.props; var publicContext = this._processContext(context); // TopLevelWrapper var Component = this._currentElement.type; ... // Initialize the public class var doConstruct = shouldConstruct(Component); // 生成TopLevelWrapper 實例 var inst = this._constructComponent( doConstruct, publicProps, publicContext, updateQueue ); ... this._instance = inst; ... var markup; ... markup = this.performInitialMount(renderedElement, hostParent, hostContainerInfo, transaction, context ... return markup; }, performInitialMount: function (renderedElement, hostParent, hostContainerInfo, transaction, context) { // TopLevelWrapper 實例 var inst = this._instance; ... // If not a stateless component, we now render if (renderedElement === undefined) { // 返回值為 ReactElement[1] renderedElement = this._renderValidatedComponent(); } // 返回 ReactNodeTypes.HOST var nodeType = ReactNodeTypes.getType(renderedElement); this._renderedNodeType = nodeType; // instantiateReactComponent.js var child = this._instantiateReactComponent( renderedElement, nodeType !== ReactNodeTypes.EMPTY /* shouldHaveDebugID */ ); this._renderedComponent = child; var markup = ReactReconciler.mountComponent( child, transaction, hostParent, hostContainerInfo, this._processChildContext(context), debugID ); ... return markup; },
當運行到var child = this._instantiateReactComponent時,就會調用上篇文章說到的instantiateReactComponent文件:
// 文件位置:src/renderers/shared/stack/reconciler/instantiateReactComponent.js function instantiateReactComponent(node, shouldHaveDebugID) { var instance; ... } else if (typeof node === "object") { ... // element.type 為 ‘h1’ if (typeof element.type === "string") { instance = ReactHostComponent.createInternalComponent(element); } return instance; }
ReactDom 會在執行的時候,執行ReactDefaultInjection.inject()將 ReactDOMComponent 注入到 ReactHostComponent 中,ReactHostComponent.createInternalComponent 最終會調用 ReactDOMComponent:
// 文件位置:src/renderers/dom/shared/ReactDomComponent.js function ReactDOMComponent(element) { // h1 var tag = element.type; validateDangerousTag(tag); // ReactElement[1] this._currentElement = element; this._tag = tag.toLowerCase(); this._namespaceURI = null; this._renderedChildren = null; this._previousStyle = null; this._previousStyleCopy = null; this._hostNode = null; this._hostParent = null; this._rootNodeID = 0; this._domID = 0; this._hostContainerInfo = null; this._wrapperState = null; this._topLevelWrapper = null; this._flags = 0; }
我們將返回的實例命名為 ReactDOMComponent[ins]。
ReactReconciler.mountComponent 會調用 ReactDomComponent 的 mountComponent 方法,這就會涉及到 HTML DOM 相關的內容,我們在下一篇進行講解。
現在我們來看一下各實例間的關系:
目前為止的調用棧:
|=ReactMount.render(nextElement, container, callback) ___ |=ReactMount._renderSubtreeIntoContainer() | |-ReactMount._renderNewRootComponent() | |-instantiateReactComponent() | |~batchedMountComponentIntoNode() upper half |~mountComponentIntoNode() (平臺無關) |-ReactReconciler.mountComponent() | |-ReactCompositeComponent.mountComponent() | |-ReactCompositeComponent.performInitialMount() | |-instantiateReactComponent() _|_ |-ReactDOMComponent.mountComponent() lower half |-_mountImageIntoNode() (HTML DOM 相關下一篇講解) _|_三、總結
本文講解了 ReactCompositeComponent[T] 轉化為 ReactDomComponent 的過程。代碼一層一層嵌套遞歸執行,最終的目的是解析Virtual Dom對象,將其轉換為 HTML。本文講解的大部分代碼是跟平臺無關的,也就是 ReactDOM 和 ReactNative 共享的。生成 HTML 的任務是由 ReactDomComponent 負責的,將在下一篇文章講解。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/98501.html
摘要:在學習源碼的過程中,給我幫助最大的就是這個系列文章,于是決定基于這個系列文章談一下自己的理解。到此為止,首次渲染就完成啦總結從啟動到元素渲染到頁面,并不像看起來這么簡單,中間經歷了復雜的層級調用。 前言 React 是一個十分龐大的庫,由于要同時考慮 ReactDom 和 ReactNative ,還有服務器渲染等,導致其代碼抽象化程度很高,嵌套層級非常深,閱讀其源碼是一個非常艱辛的過...
摘要:調用棧是這樣的這里生成的我們將其命名為,它將作為參數傳入到。整個的調用棧是這樣的組件間的層級結構是這樣的到此為止,頂層對象已經構造完畢,下一步就是調用來自的方法,進行頁面的渲染了。通過表達的結構最終會轉化為一個純對象,用于下一步的渲染。 歡迎關注我的公眾號睿Talk,獲取我最新的文章:showImg(https://segmentfault.com/img/bVbmYjo); 一、前言...
摘要:本篇開始介紹自定義組件是如何渲染的。組件將自定義組件命名為,結構如下經過編譯后,生成如下代碼構建頂層包裝組件跟普通元素渲染一樣,第一步先會執行創建為的。調用順序已在代碼中注釋。先看圖,這部分內容將在下回分解 前言 React 是一個十分龐大的庫,由于要同時考慮 ReactDom 和 ReactNative ,還有服務器渲染等,導致其代碼抽象化程度很高,嵌套層級非常深,閱讀其源碼是一個非...
摘要:的和真正有效的都各只有一行代碼的調用棧如下這中間的函數調用邏輯很清晰,最終會走到這里這里的邏輯很簡單,如果不是數組,則調用回調函數如果是數組,則繼續調用自身,相當于深度優先遍歷。這里的回調函數就是中的這里直接調用,創建。 前言 React 是一個十分龐大的庫,由于要同時考慮 ReactDom 和 ReactNative ,還有服務器渲染等,導致其代碼抽象化程度很高,嵌套層級非常深,閱讀...
摘要:前言是一個十分龐大的庫,由于要同時考慮和,還有服務器渲染等,導致其代碼抽象化程度很高,嵌套層級非常深,閱讀其源碼是一個非常艱辛的過程。在學習源碼的過程中,給我幫助最大的就是這個系列文章,于是決定基于這個系列文章談一下自己的理解。 前言 React 是一個十分龐大的庫,由于要同時考慮 ReactDom 和 ReactNative ,還有服務器渲染等,導致其代碼抽象化程度很高,嵌套層級非常...
閱讀 1540·2023-04-26 00:20
閱讀 1130·2023-04-25 21:49
閱讀 808·2021-09-22 15:52
閱讀 583·2021-09-07 10:16
閱讀 976·2021-08-18 10:22
閱讀 2671·2019-08-30 14:07
閱讀 2243·2019-08-30 14:00
閱讀 2658·2019-08-30 13:00