上一章jsx语法是如何解析的讲到了node
<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
我暂时没弄的太明白,可是它主要做用是优化组件的更新,因此不影响咱们理解组件的挂载.好了,下面进入正题.react
看看下面的代码如何执行的bash
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
方法 它主要分为二个部分app
// 第一部分
var componentInstance = instantiateReactCompone
nt(
nextElement,
false
);
// 第二部分
ReactUpdates.batchedUpdates(
batchedMountComponentIntoNode,
componentInstance,
container,
shouldReuseMarkup,
context
);
复制代码
instantiateReactComponent
instantiateReactComponent
依据不一样的类型产生不一样的实例,每一个实例都具备mountComponent方法核心方法.dom
类型 | 对应方法 |
---|---|
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
就挂载完成了.post
在前面·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
);
复制代码
具体来看看第二个方法ui
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
的生命周期