我们这里跳过第二部分,先来看一下第三部分。 第二部分是在 根元素 container 内已经有组件存在的情况的处理逻辑,
第三部分则是初始情况下,container内是什么也没有的情况的处理逻辑。
先看看第三部分都有什么
_renderSubtreeIntoContainer 1
_renderSubtreeIntoContainer:function(parentComponent, nextElement, container, callback){
'
看一下, getReactRootElementInContainer方法在下边单独罗列这个方法的作用是。
这个方法是根据 container 的类型来返回对应的DOM。
如果 container 类型是document,就返回整个 document文档。
如果 container 类型是普通的元素 比如div什么的,就返回他的第一个子元素。
初始情况下,reactRootElement 应该是个空。
'
var reactRootElement = getReactRootElementInContainer(container);
'
containerHasReactMarkup 在初始挂载的时候同样为 false
'
var containerHasReactMarkup = reactRootElement && !!internalGetID(reactRootElement);
'
当然初始的情况下 containerHasNonRootReactChild 的值也是false。
hasNonRootReactChild方法 简单的来说就是判断 当前的 node 也就是 container 是否有根子组件。
'
var containerHasNonRootReactChild = hasNonRootReactChild(container);
'
表示是否应该重新标记一下 初始的时候 也是 false
'
var shouldReuseMarkup = containerHasReactMarkup && !prevComponent && !containerHasNonRootReactChild;
'
这一部分的内容,请往下看
'
var component = ReactMount._renderNewRootComponent(
nextWrappedElement, container,
shouldReuseMarkup,
parentComponent != null
? parentComponent._reactInternalInstance.
_processChildContext(parentComponent._reactInternalInstance._context)
: emptyObject)._renderedComponent.getPublicInstance();
if (callback) {
callback.call(component);
}
return component;
}
我们先来看看这一部分用到的方法。
getReactRootElementInContainer 方法
'
这个方法是根据 container 的类型来返回对应的DOM。
如果 container 类型是document,就返回整个 document文档。
如果 container 类型是普通的元素 比如div什么的,就返回他的第一个子元素。
'
function getReactRootElementInContainer(container) {
if (!container) {
return null;
}
// DOC_NODE_TYPE = 9;
// Document.nodeType==9
if (container.nodeType === DOC_NODE_TYPE) {
// document.documentElement则是整个HTML文件
return container.documentElement;
} else {
return container.firstChild;
}
}
_renderSubtreeIntoContainer 2
这一部分是这一部分的核心,我们单独的摘出来说:
parentComponent 是 null.
这里调用 _renderNewRootComponent 方法返回一个 component。
这里的component 是调用了_renderedComponent.getPublicInstance()之后的。
getPublicInstance 是 返回组件的公开表示形式。
比如下边的图:
这里依照惯例,调用了另外的一个方法 _renderNewRootComponent
_renderSubtreeIntoContainer: function (parentComponent, nextElement, container, callback) {
var component = ReactMount._renderNewRootComponent(
nextWrappedElement, container,
shouldReuseMarkup,
parentComponent != null
? parentComponent._reactInternalInstance.
_processChildContext(parentComponent._reactInternalInstance._context)
: emptyObject)._renderedComponent.getPublicInstance();
if (callback) {
callback.call(component);
}
return component;
}
所以来看看 _renderNewRootComponent 方法
_renderNewRootComponent 方法在第一部分已经有过说明,这里再说一次吧。
_renderNewRootComponent
这里 instantiateReactComponent 方法的使用 参考文章Element 实例化部分
_renderNewRootComponent: function (nextElement, container, shouldReuseMarkup, context) {
'
instantiateReactComponent 类会根据 nextElement 的类型来选择不同的类来将其实例化为一个 挂载实例。
'
var componentInstance = instantiateReactComponent(nextElement, null);
'
而后将 实例化的 componentInstance 通过 _registerComponent 方法 放入到 instancesByReactRootID 中。
'
var reactRootID = ReactMount._registerComponent(componentInstance, container);
'
采用批处理来进行处理。
'
ReactUpdates.batchedUpdates(batchedMountComponentIntoNode, componentInstance, reactRootID, container, shouldReuseMarkup, context);
'
返回一个 componentInstance
'
return componentInstance;
}
ReactUpdates.batchedUpdates 方法我们之前说过参考文章。简单的来说就是 使用批处理执行该方法中的第一个参数(是个函数),该方法中的后边几个参数作为第一个参数执行时候的参数。
再简单的来说就是 以 componentInstance, reactRootID, container, shouldReuseMarkup, context几个为参数调用方法 batchedMountComponentIntoNode。
所以我们还是来看看 batchedMountComponentIntoNode 方法
batchedMountComponentIntoNode
这里用到了调和事务,关于事务请参考这篇文章
首先 取出来一个 ReactReconcileTransaction 也叫做调和事务 的实例出来使用。这里使用了事务和池化技术
function batchedMountComponentIntoNode(componentInstance, rootID, container, shouldReuseMarkup, context) {
var transaction = ReactUpdates.ReactReconcileTransaction.getPooled(shouldReuseMarkup);
'
使用 调和事务来包装 mountComponentIntoNode 方法。
'
transaction.perform(mountComponentIntoNode, null, componentInstance, rootID, container, transaction, shouldReuseMarkup, context);
'
调和事务使用完之后放回 '池'中。
'
ReactUpdates.ReactReconcileTransaction.release(transaction);
}
batchedMountComponentIntoNode 方法的核心是使用事务 调用了 mountComponentIntoNode方法,下边我们来看看 mountComponentIntoNode 方法。
看一下 mountComponentIntoNode 方法
function mountComponentIntoNode(componentInstance, rootID, container, transaction, shouldReuseMarkup, context) {
'
这里 会调用 ReactReconciler.mountComponent 方法生成一个 markup。markup就是一串html字符串。
比如:
<div data-reactid=".0">
<span data-reactid=".0.0">this is app </span>
<span data-reactid=".0.1">13</span>
<div data-reactid=".0.2">Hello </div>
</div>
'
var markup = ReactReconciler.mountComponent(componentInstance, rootID, transaction, context); -----(1)
'
将 componentInstance 赋值给 componentInstance._renderedComponent._topLevelWrapper
'
componentInstance._renderedComponent._topLevelWrapper = componentInstance;
'
这里调用 _mountImageIntoNode 方法将 markup 也就是当前的全部 html挂载到 container中。
'
ReactMount._mountImageIntoNode(markup, container, shouldReuseMarkup, transaction);
}
ReactReconciler.mountComponent
这里该方法会根据 internalInstance 也就是 componentInstance(挂载实例) 来执行其 mountComponent 方法。这里可以参考文章Element实例化
方法最终返回一个 markup。 而关于,不同类型的 internalInstance 比如React自定义组件生成的 internalInstance 或者是 div 生成的 internalInstance 等,有不同的 mountComponent方法。,请参考文章生成markup
ReactReconciler.mountComponent 方法如下
mountComponent: function (internalInstance, rootID, transaction, context) {
'
调用挂载实例的 mountComponent 方法生成 markup
'
var markup = internalInstance.mountComponent(rootID, transaction, context);
if (internalInstance._currentElement && internalInstance._currentElement.ref != null) {
transaction.getReactMountReady().enqueue(attachRefs, internalInstance);
}
return markup;
},
_mountImageIntoNode 方法
'
核心是调用了 setInnerHTML 方法,将markup放置在 container内。
'
if (transaction.useCreateElement) {
while (container.lastChild) {
container.removeChild(container.lastChild);
}
container.appendChild(markup);
} else {
setInnerHTML(container, markup);
}
setInnerHTML 方法具体做了什么请看下边的。
setInnerHTML 方法
'
直接将 makeup 以 innerHTML 的形式注入
'
var setInnerHTML = function (node, html) {
node.innerHTML = html;
};
总结
到此将下边这行代码做的事情做完了。
ReactDOM.render(<App />, document.getElementById("app"));
组件被插入了 container 也就是id为app的div内。