React组件插入DOM流程(源码分析)

 自我总结:
生命周期是在调用mountComponent和updateComponent里调用的,那么mountComponent和updateComponent目测会在ReactDom.render 和setState等会被调到
总结如图:

 


注意(自解):在ReactElement得到ReactComponent的过程中,根据type不同创建四种不同的ReactComponent。基本每种ReactComponent都有各自自己大致的mountComponent,receiveComponen, updateComponent,unmountComponent接口好像,
他们会引发React生命周期方法的调用。

1.
ReactDOM.render(
<h1>Hello, world!</h1>,
document.getElementById('example')
);
经过babel转码后为

ReactDOM.render(
React.createElement(
'h1',   // type, DOM原生组件的type为string,React自定义组件type为Object
null,   // config,会设置到ref,key,props中
'Hello, world!'   // children,子组件.这儿为文本组件
),
document.getElementById('example')
)


2.ReactDOM.render()实际调用ReactMount.render(),接着调用到ReactMount._renderSubtreeIntoContainer().
/**
* 将ReactElement插入DOM中,并返回ReactElement对应的ReactComponent。
* ReactElement是React元素在内存中的表示形式,可以理解为一个数据类,包含type,key,refs,props等成员变量
* ReactComponent是React元素的操作类,包含mountComponent(), updateComponent()等很多操作组件的方法
*
* @param {parentComponent} 父组件,对于第一次渲染,为null
* @param {nextElement} 要插入到DOM中的组件,对应上面例子中的<h1>Hello, world!</h1>经过babel转译后的元素
* @param {container} 要插入到的容器,对应上面例子中的document.getElementById('example')获取的DOM对象
* @param {callback} 第一次渲染为null
*
* @return {component}  返回ReactComponent,对于ReactDOM.render()调用,不用管返回值。
*/
_renderSubtreeIntoContainer: function (parentComponent, nextElement, container, callback)
在这个方法内部,拿到prevComponent,再转换出prevElement,用diff算法判断到底是要更新组件(updateRootComponent方法更新老组件。然后返回老的ReactComponent)还是 先销毁再重创组件(renderNewRootComponent方法创建新组件。然后返回新的ReactComponent)

3.自解:
其中ReactMount._renderNewRootComponent内部,先初始化ReactComponent,根据ReactElement中不同的type字段,创建不同类型的组件对象,即ReactComponent,然后批量更新函数里传入这个ReactComponent和batchedMountComponentIntoNode,
后面batchedMountComponentIntoNode以transaction事务的形式调用mountComponentIntoNode,这个方法的内部再调用对应ReactComponent中的mountComponent方法来渲染组件
// mountComponent返回React组件解析的HTML。不同的ReactComponent的mountComponent策略不同,可以看做多态
上面的<h1>Hello, world!</h1>, 对应的是ReactDOMTextComponent,最终解析成的HTML为
// <h1 data-reactroot="">Hello, world!</h1>
然后将解析出来的HTML通过mountImageIntoNode方法插入DOM中,
这个内部   // 将markup这个HTML设置到container这个DOM元素的innerHTML属性上,这样就插入到了DOM中了
setInnerHTML(container, markup);
// 将instance这个ReactComponent渲染后的对象,即Virtual DOM,保存到container这个DOM元素的firstChild这个原生节点上。简单理解就是将Virtual DOM保存到内存中,这样可以大大提高交互效率

3.  // React DOM diff算法
if (prevType === 'string' || prevType === 'number') {
// 如果前后两次为数字或者字符,则认为只需要update(处理文本元素),返回true
return (nextType === 'string' || nextType === 'number');
} else {
// 如果前后两次为DOM元素或React元素,则必须type和key不变(key用于listView等组件,很多时候我们没有设置key,故只需type相同)才update,否则先unmount再重新mount。返回false
return (
nextType === 'object' &&
prevElement.type === nextElement.type &&
prevElement.key === nextElement.key
);
}

自解:diff 算法的参数就是数据对象,也就是之前那个element(ReactElement)


7.总结
ReactDOM.render()是渲染React组件并插入到DOM中的入口方法,它的执行流程大概为

React.createElement(),创建ReactElement对象。他的重要的成员变量有type,key,ref,props。这个过程中会调用getInitialState(), 初始化state,只在挂载的时候才调用。后面update时不再调用了。

instantiateReactComponent(),根据ReactElement的type分别创建ReactDOMComponent, ReactCompositeComponent,ReactDOMTextComponent等对象

mountComponent(), 调用React生命周期方法解析组件,得到它的HTML。

_mountImageIntoNode(), 将HTML插入到DOM父节点中,通过设置DOM父节点的innerHTML属性。

缓存节点在React中的对应对象,即Virtual DOM。 
posted @ 2018-07-02 18:45  River89397380  阅读(1545)  评论(0)    收藏  举报