ReactDOM.render--- 第一部分 初始挂载

326 阅读4分钟

我们先来解析 _renderSubtreeIntoContainer 方法的第一部分

第一部分的核心是 生成了一个 nextWrappedElement 而关于 nextWrappedElement 参考文章 Element总结篇

简单的来说 nextWrappedElement 就是将 下边的跟元素或者叫做 container。转换为Element。

    <div id="app"></div>

在生成nextWrappedElement的时候,加入了参数 nextElement,而这个nextElement就是 我们再 项目main.js或者是index.js 文件中的 下边这个代码的 ' <App/>'组件。

    ReactDOM.render(<App />, document.getElementById("app"));

prevComponent 就是查看一下,当前的 根元素 id为app的div内是不是已经有组件了,也就是判断是初始化不是。

    _renderSubtreeIntoContainer: function (parentComponent, nextElement, container, callback) {
        var nextWrappedElement = new ReactElement(TopLevelWrapper, null, null, null, null, null, nextElement);
        
        var prevComponent = instancesByReactRootID[getReactRootID(container)];
    }

这一部分就包含了两行代码。我们一行行来看

TopLevelWrapper

    var nextWrappedElement = new ReactElement(TopLevelWrapper, null, null, null, null, null, nextElement);
    
    '
        这里先生成了一个 WrappedElement 也就是一个被包装过的element。
        其次:TopLevelWrapper 是一个函数,以一个函数来作为type 来 createElement。这就生成了一个 WrappedElement。
        你说element就element呗,啥是被包装的element呢?我们知道React组件生成的就是一个真实的 element,而 WrappedElement 是一个本身不是React组件的html硬生生的变成element,如果是单页面应用,那就只有一个 <div id="app"></div> 需要这么搞。
        
        简单的来说是将包裹React根组件的 容器 container,包装成了element。
    '
    
     

继续往下看

instancesByReactRootID

    
    var prevComponent = instancesByReactRootID[getReactRootID(container)];
    
    '
        根据container找到 RootID.
        
        如果 container是一个document 就返回整个文档,而后根据整个文档,去找其上的属性 data-reactid.
        如果只是普通的元素,就找到container的第一个子元素返回。而后根据这个子元素找到其上的 data-reactid属性。
        一般,在初始情况下,container是不包含任何内容的。
        当你更新组件的时候也就是将当前 container下的跟组件换掉的时候,这个时候 getReactRootID(container) 返回的是 上一次跟组件的 data-reactid属性值。
    '

_registerComponent 方法

要想知道 instancesByReactRootID 有什么东西,就看谁给了它什么东西。

    _registerComponent: function (nextComponent, container) {
     
        var reactRootID = ReactMount.registerContainer(container);
        
        instancesByReactRootID[reactRootID] = nextComponent;
        '
            从这里看到 instancesByReactRootID 里的内容是 nextComponent。而要知道 nextComponent 是什么,就得知道谁给了 _registerComponent,也就是谁调用了它。
        '
        return reactRootID;
    },

_renderNewRootComponent 方法

    _renderNewRootComponent: function (nextElement, container, shouldReuseMarkup, context) {
        '
            根据 nextElement 生成了 componentInstance.
            nextElement是将要挂载的 element。该方法被调用是在我们的第三部分,这里先不说了。
            总之你只需要知道nextElement 就是一个 element。
            看一下 instantiateReactComponent方法是如何生成 componentInstance的。
        '
        var componentInstance = instantiateReactComponent(nextElement, null);
        
        '
            所以 instancesByReactRootID 里的内容其实就是 componentInstance.
        '
        var reactRootID = ReactMount._registerComponent(componentInstance, container);
            
    }
    

instantiateReactComponent 模块

该模块提供了一个方法 instantiateReactComponent,该方法会将 一个 element 对象实例化为一个 ReactCompositeComponentWrapper 如下图所示:

ReactCompositeComponentWrapper 较之我们 初始提供的 element 多了很多东西。

instantiateReactComponent方法会根据 提供的element的类型不同,选择不同的处理方式。比如:

element 是 null

    
  if (node === null || node === false) {
    instance = new ReactEmptyComponent(instantiateReactComponent);
  } 

ReactEmptyComponent方法

    '
        instantiate参数还是 instantiateReactComponent 方法。
        这个函数初始化了一些属性给 instance实例。
        值得注意的是 _renderedComponent属性。
        
        这里 placeholderElement 是注入进来的一个参树,值为字符串 'noscript'。
        也就是说,我们再组件的render方法中返回了一个 null或者是false,就会得到一个实例。
        instantiateReactComponent('noscript')
        
    '
    var ReactEmptyComponent = function (instantiate) {
        this._currentElement = null;
        this._rootNodeID = null;
        this._renderedComponent = instantiate(placeholderElement);
    };

element 是 string

    else if (typeof node === 'string' || typeof node === 'number') {
        instance = ReactNativeComponent.createInstanceForText(node);
    }
ReactNativeComponent模块的createInstanceForText
    function createInstanceForText(text) {
        return new textComponentClass(text);
    }
textComponentClass 方法

首先是textComponentClass是从 ReactDefaultInjection中注入的 ReactDOMTextComponent 模块

ReactDOMTextComponent模块是一个构造函数,构造函数的原型上加了很多的属性,比如一个 construct方法

    construct: function (text) {
            this._currentElement = text;
            this._stringText = '' + text;
    
            
            this._rootNodeID = null;
            this._mountIndex = 0;
    },:
    

当element 是 string的时候instantiateReactComponent方法 返回了一个 ReactDOMTextComponent的实例。

instantiateReactComponent方法会根据不同的 element特点选择不同的 构造函数来构造ReactComponent实例。

ok 就此打住,关于Element转换为实例的具体的内容请参考文章Element 实例化部分的内容

instancesByReactRootID 中的内容就是 某个组件的实例,实例中包含了一些有用的属性,比如 _currentElement 指向当前组件的 element。

总结一下:

instancesByReactRootID在初始的情况下是没有东西的。

在第三部分的时候,往 instancesByReactRootID 对象里塞入了东西。

instancesByReactRootID 存的只是根组件,其他的组件是不会往里边存的。

以上便是挂载核心方法的第一部分的内容。