React源代码解析(2):组件的挂载

栏目: 服务器 · 发布时间: 5年前

内容简介:上一章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方法核心方法.

类型 对应方法
nullundefined 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 的生命周期


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Charlotte's Web

Charlotte's Web

E. B. White / Scholastic / 2004 / USD 0.01

This is the tale of how a little girl named Ferm, with the help of a friendly spider, saved her pig, Wilbur, from the usual fate of nice fat little pigs.一起来看看 《Charlotte's Web》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具