内容简介:上一章jsx语法是如何解析的讲到了jsx语法是如何解析为虚拟看看下面的代码如何执行的
上一章jsx语法是如何解析的讲到了
<div> <div>1</div> <div>2</div> <div>3</div> </div> 复制代码
jsx语法是如何解析为虚拟 dom
的,接下来我将聊聊虚拟 dom
是如何挂载到真实 dom
上的。 我读的是 React^15.6.2
的源代码,因为最新的 React^16.6.3
版本,引入了 Fiber架构
,因为时间有限, Fiber
我暂时没弄的太明白,但是它主要作用是优化组件的更新,所以不影响我们理解组件的挂载.好了,下面进入正题.
看看下面的代码如何执行的
import React from "./lib/react"; import {render} from "./lib/react-dom"; const Child = () => <div> Child </div> class App extends React.Component { constructor(props){ super(props); this.state = { name:"leiwuyi" } } render(){ return ( <div>App</div> ) } } render(<div className="leiwuyi"> <div>1</div> <div>2</div> <div>3</div> <App ></App> </div>, document.getElementById("root")); 复制代码
ReactDom.render()
其实是运行了 _renderSubtreeIntoContainer
它接收了 nextElement
, container
参数. 首选将 container
这个 Element
类型的节点包装成 Component
类型,
var nextWrappedElement = React.createElement( TopLevelWrapper, { child: nextElement } ); 然后执行ReactMount._renderNewRootComponent( nextWrappedElement, container, shouldReuseMarkup, nextContext ) 复制代码
这个方法执行完毕,其实组件就已经挂载到 root
节点上去了,来看看 ReactMount._renderNewRootComponent
方法
它主要分为二个部分
// 第一部分 var componentInstance = instantiateReactCompone nt( nextElement, false ); // 第二部分 ReactUpdates.batchedUpdates( batchedMountComponentIntoNode, componentInstance, container, shouldReuseMarkup, context ); 复制代码
第一部分 instantiateReactComponent
instantiateReactComponent
依据不同的类型产生不同的实例,每个实例都具有mountComponent方法核心方法.
类型 | 对应方法 |
---|---|
null
或 undefined
|
ReactEmptyComponent
|
元素类型 |
ReactHostComponent.createInternalComponent( element)
|
component
的类型 |
ReactCompositeComponent
|
文本类型 | `ReactHostComponent.createInstanceForText(node) |
ReactHostComponent.createInternalComponent( element)
最终是运行的 ReactDOMComponent
.
第二部分 MountComponentIntoNode
就是通过事务的形式来运行 mountComponentIntoNode
方法
而 mountComponentIntoNode
方法最终是运行实例的 mountComponent
方法。
该方法会产生对应的真实 dom
节点 markup
;
产生之后然后通过 setInnerHTML(container, markup)
;插入到 container
里面.
简单的讲就是拿到真实 dom
插入到 container
里面去,这段代码执行完毕真实 dom
就挂载完成了.
具体的实现过程
在前面· nextElement
包装成 Component
类型·,所以最终产生的实例的原型对象是 ReactCompositeComponent
实例有了那么接下来就是执行 componentInstance.mountComponent方法
;
该方法执行的 ReactCompositeComponent.mountComponent
接下来看看该方法到底做了什么. 下面我就几个核心方法进行一个说明.
ReactCompositeComponent.mountComponent
第一个方法 实例化组件 this._constructComponent( doConstruct, publicProps, publicContext, updateQueue ) 产生组件的实例 如 <App > ---> new App() 就是我们常在组件中使用的this对象 第二个方法 拿到组件对应的真实dom markup = this.performInitialMount( renderedElement, hostParent, hostContainerInfo, transaction, context ); 复制代码
具体来看看第二个方法
this.performInitialMount
// 调用this.render或者func() 拿到虚拟dom, if (renderedElement === undefined) { renderedElement = this._renderValidatedComponent(); } // 拿到一个element类型的componentInstance(上文中有的) var child = this._instantiateReactComponent( renderedElement, nodeType !== ReactNodeTypes.EMPTY /* shouldHaveDebugID */ ); this._renderedComponent = child; // 拿到真实dom var markup = ReactReconciler.mountComponent( child, transaction, hostParent, hostContainerInfo, this._processChildContext(context), debugID ); 复制代码
这里面的 ReactReconciler.mountComponent
其实是调用的 ReactDOMComponet.mountComponent
方法。
所以 this.performInitialMount
简单的讲就是 拿到组件的虚拟 dom
,然后获取它对应 的componentInstance
实例然后执行 ReactDOMComponet.mountComponent
拿到真实 dom
所以我们接下来要看看 ReactDOMComponet.mountComponent
方法的实现过程
ReactDOMComponet.mountComponent
函数前面一部分其实对 tag
的类型进行了处理 我们直接略过把 tag
当做 div
创建了一个div节点 el = ownerDocument.createElement( this._currentElement.type ); // 设置该节点的属性 this._updateDOMProperties( null, props, transaction ); // 构建它孩子的真实dom然后插入到该节点中去 var lazyTree = DOMLazyTree(el); this._createInitialChildren( transaction, props, context, lazyTree ); 所以执行之后lazyTree就是一个完整的真实dom节点 复制代码
我们来看看 this._createInitialChildren
方法,
核心代码在这里
var mountImages = this.mountChildren( childrenToUse, transaction, context ); 执行的是 var children = this._reconcilerInstantiateChildren( nestedChildren, transaction, context ); // 产生child对应的实例 for (var name in children) { if (children.hasOwnProperty(name)) { var child = children[name]; var selfDebugID = 0; if ( "development" !== "production" ) { selfDebugID = getDebugID(this); } var mountImage = ReactReconciler.mountComponent( child, transaction, this, this._hostContainerInfo, context, selfDebugID ); child._mountIndex = index++; mountImages.push(mountImage); } } 复制代码
看到这里,就已经很明显这是一个深度优先遍历;它的 child
又产生了一个实例然后执行 mountComponent
方法拿到真实 dom
,直到拿到最后一级孩子的真实 dom
然后不断向上递归插入父级。直到插入到最顶层这样就拿到 了Component
的真实 dom
。最后插入到 container
容器里面.
下一章将聊聊 React
的生命周期
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。