歡迎關注個人公衆號睿Talk
,獲取我最新的文章:
javascript
React 是一個十分龐大的庫,因爲要同時考慮 ReactDom 和 ReactNative ,還有服務器渲染等,致使其代碼抽象化程度很高,嵌套層級很是深。閱讀 React 源碼是一個很是艱辛的過程,在學習過程當中給我幫助最大的就是這個系列文章。做者對代碼的調用關係梳理得很是清楚,並且還有配圖幫助理解,很是值得一讀。站在巨人的肩膀之上,我嘗試再加入本身的理解,但願對有志於學習 React 源碼的讀者帶來一點啓發。java
本系列文章基於 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 算法詳解node
上一篇文章中,介紹了頂層對象ReactCompositeComponent[T]
是如何構造的,接下來咱們看看 batchedMountComponentIntoNode 作了什麼事情。算法
本文將要講解的調用棧是下面這個樣子的:segmentfault
|=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 生成:app
// 文件位置: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; }
如今各實例間的關係是這樣的:less
再繼續看 mountComponent 方法:dom
// 文件位置: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 負責的,將在下一篇文章講解。