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

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

内容简介:上一章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 的生命周期


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

查看所有标签

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

Python Algorithms

Python Algorithms

Magnus Lie Hetland / Apress / 2010-11-24 / USD 49.99

Python Algorithms explains the Python approach to algorithm analysis and design. Written by Magnus Lie Hetland, author of Beginning Python, this book is sharply focused on classical algorithms, but it......一起来看看 《Python Algorithms》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具