ReactDOMComponent
虽然整体的逻辑看起来异常复杂,但是仔细分析,实际上它处理的事情也仍然很清晰。
这件简要分析
line461-line482简单的定义了ReactDOMComponent内部的私有变量。
line1203-line1207的Object.assign则很简单将ReactDOMComponent.Mixin&ReactMultiChild.Mixin
复制到ReactDOMComponent
的原型上。所以这里需要对ReactDOMComponent.Mixin&ReactMultiChild.Mixin
做一些分析
ReactDOMComponent.Mixin
API | 说明 |
---|---|
mountComponent | 生成根标记然后递归生成内部标记。这个生成是在事务支持下的生成(更新前卸载事件更新后重新绑定诸如此类)。这个操作不是冥等的,多次执行结果并不完全一致。 |
_createOpenTagMarkupAndPutListeners | 生成html标记的openTag markup并处理事件绑定 |
_createContentMarkup | 为html标签内的内容创建对应的markup |
_createInitialChildren | 遍历下级children标记放到目标元素中 |
receiveComponent | 更新_currentElement并调用this.updateComponent更新组件 |
updateComponent | 使用_updateDOMProperties & _updateDOMChildren更新props、styles和children |
_updateDOMProperties | 更新styles和props |
_updateDOMChildren | 更新children |
getHostNode | 获取组件对应的HTMLElement节点 |
unmountComponent | 卸载事件注册 但不直接影响DOM |
getPublicInstance | 获取组件对应的HTMLElement节点 同getHostNode |
mountComponent
生成根标记然后递归生成内部标记。这个生成是在事务支持下的生成(更新前卸载事件更新后重新绑定诸如此类)。这个操作不是冥等的,多次执行结果并不完全一致。
关于事务的分析,我们后面再总体分析一下看看,这里简单看看这里的遍历生成标记是如何做到的。
首先看看mountComponent整体执行完毕后的返回数据结构
其次,这个函数里面有两行非常关键的代码,这是遍历生成标记的核心调用
1 | var lazyTree = DOMLazyTree(el); |
这里看看lazyTree返回值:
1 | function DOMLazyTree(node) { |
然后这里接下来先看看_createInitialChildren
_createInitialChildren
它的作用是遍历下级children标记放到目标元素中
先看看大致逻辑和参数问题,这里有transaction, props, context, lazyTree
这里props是实例化ReactDOMComponent时候传入的element,transaction、context这里不再讲,lazyTree是上一步传入。此时props上显然会有children保存其子元素的数组。
这里有两个逻辑分支
props.children 是string|number -> contentToUse 此时DOMLazyTree.queueText(lazyTree, contentToUse)
否则childrenToUse执行
下一步:
1
2
3
4
5
6
7
8var mountImages = this.mountChildren(
childrenToUse,
transaction,
context,
);
for (var i = 0; i < mountImages.length; i++) {
DOMLazyTree.queueChild(lazyTree, mountImages[i]);
}
这个this.mountChildren在ReactMultiChild.Mixin被集成到原型上来。
1 | mountChildren: function(nestedChildren, transaction, context) { |
这里暂不用去理会this._reconcilerInstantiateChildren
只是简单提一下,它是一个数组,里面的元素的结构和lazyTree返回值是一致的。
仅仅看ReactReconciler.mountComponent就可以知道,mountComponent里面说到的这个遍历,便是从这里而来。他的流程是:
1 | ReactDOMComponent.mountComponent |
至于ReactReconciler.mountComponent 到 ReactDOMComponent.mountComponent这个环节,请参考上文ReactReconciler.mountComponent。
这里的遍历是起到这样的作用。我们知道jsx转换到js之后是近似
的嵌套结构,如果有子组件那么也是这样的结构放在props.children上,以此类推。
而这里的遍历则是将这个结构转换成
1 | { |
这样的结构,子元素放到children数组中。只不过此时这里的node已经是HTML元素了可以直接插入到DOM中。
这时候使用ReactMount._mountImageIntoNode就可以将其渲染到DOM了。
PS: 需要注意的,这个转换仅仅是在transaction.useCreateElement
成立(在这里,服务端渲染才会为fasle,这里暂不考虑这块),否则回直接返回html标记字符串。
_updateDOMChildren
_updateDOMChildren这个方法是当children更新后的调用。它在updateComponent环节被调用过。
在这个函数中有个调用非常关键 this.updateChildren
,它从ReactMultiChild.Mixin
继承而来。这里设计到了diff算法处理,暂且放下。
_createOpenTagMarkupAndPutListeners
这个函数可以返回一个<div class="name" />
代码中的<div class="name"
这个部分。并对事件进行绑定。服务端渲染会用到。
_createContentMarkup
为html标签内的内容创建对应的markup。服务端渲染会用到。
和_createOpenTagMarkupAndPutListeners有一些关联,前者是获得openTagMarkup,这个获取标记中间的元素内容Markup。
updateComponent
1 | this._updateDOMProperties(lastProps, nextProps, transaction); |
unmountComponent
关键代码是:
1 | this.unmountChildren(safely); |
主要涉及了ReactDOMComponentTree、EventPluginHub、ReactMultiChild三个模块
getHostNode
调用了ReactDOMComponentTree.getNodeFromInstance获取组件对应的HTMLElement节点。这个模块后面分析。
_updateDOMProperties
这个函数其实还挺重要的。在我们的使用过程中,我们在props上放style放className 放refs放onClick。这些都会得到体现。
这里有三个逻辑分支
- style的处理
- 事件注册的处理[registrationNameModules.hasOwnProperty(propKey)]。先解绑后重新绑定。关键函数enqueuePutListener
- 自定义标签的处理