Element 为Object:type为function

383 阅读9分钟

在 instantiateReactComponent 模块中,我们根据 Element的不同来选择不同的方法来处理element为挂载实例,其中当Element为Object的时候,Element.type分为string,number,除此之外的为一种情况也就是type为function的时候。

一般Element.type为function指的是我们自定义的组件。 比如 的type 为 f APP(props)是一个函数。

这种情况下才有如下的方式来处理。

    instance = new ReactCompositeComponentWrapper();

ReactCompositeComponentWrapper

ReactCompositeComponentWrapper 是一个构造函数,assign方法则为该构造函数添加了原型属性。 这里着重说一下 { _instantiateReactComponent: instantiateReactComponent } 为构造函数加入了 原型方法 instantiateReactComponent,自定义的组件终究是要转换为HTMl组件才能被浏览器认识,而一个自定义的组件终究是由HTML组件组成的。 这个方法赋值给构造方法的原型,则是用来递归使用 instantiateReactComponent的。 具体如何使用,往下看。

    var ReactCompositeComponentWrapper = function () {};
    assign(
        ReactCompositeComponentWrapper.prototype,             ReactCompositeComponent.Mixin, 
        {
            _instantiateReactComponent: instantiateReactComponent
        }
    );

这里往 ReactCompositeComponentWrapper 构造函数添加的原型属性的主要内容是 ReactCompositeComponent。接下来先看看ReactCompositeComponent

ReactCompositeComponent.Mixin

与前边的文章

element 为 Object:type为 string

element 为string或者是number

在结构上是相似的,都会拥有自己的 mountComponent ,unmountComponent,updateComponent 等方法,但是,不同的element也会有属于自己的独特方法。我们先来看看Element 为Object:type为function的时候的组件(也就是自定义组件)的挂载实例长什么样子。

如上图所示,一个APP组件的样子。

以下是 ReactCompositeComponentMixin 所暴露出来的方法,相比之下,自定义组件初始化挂载实例多了些方法。 接下来我们一个个的来看看这些方法。

    var ReactCompositeComponentMixin = {
        construct:function(){},
        mountComponent: function (rootID, transaction, context) {},
        unmountComponent: function () {},
        receiveComponent: function (nextElement, transaction, nextContext) {},
        performUpdateIfNecessary: function (transaction) {},
        updateComponent: function (transaction, prevParentElement, nextParentElement, prevUnmaskedContext, nextUnmaskedContext) {},
        
        attachRef: function (ref, component) {},
        detachRef: function (ref) {},
        getName: function () {},
        
        getPublicInstance: function () {},
    }
    

construct

该方法的作用是初始化一些属性,这些属性的作用不同:

在 ReactUpdateQueue 模块使用的属性:

        this._pendingElement = null;
        this._pendingStateQueue = null;
        this._pendingReplaceState = false;
        this._pendingForceUpdate = false;
    
        this._renderedComponent = null;
    
        this._context = null;
        this._mountOrder = 0;
        this._topLevelWrapper = null;
    

在 ReactUpdates and ReactUpdateQueue. 中使用的属性

    this._pendingCallbacks = null;

mountComponent 1

mountComponent 与之前的挂载实例类型一样,主要的作用便是生成 markup。

我们知道,markup就是html标签的string表示,但是,一个自定义的组件浏览器是不认的,这里就要将其转换为 html标签的string表示。

    mountComponent: function (rootID, transaction, context) {
        '
            为construct 方法初始化的部分属性赋值。
            nextMountID初始值为1,以此递增。
        '
        this._context = context;
        this._mountOrder = nextMountID++;
        this._rootNodeID = rootID;
        
        
        '
          _processProps就是在 当前环境不是production的时候,校验了一下props的类型是否合规。该方法的核心作用只是返回element的 props
        '
        var publicProps = this._processProps(this._currentElement.props);
        '
            同样的的得到 context
        '
        var publicContext = this._processContext(context);
        
        '
            这里拿到 自定义组件的type,这个type肯定是函数,比如 ƒ App(props) 上边的图中所示
        '
        var Component = this._currentElement.type;
        
        '
            定义两个变量。
        '
        var inst;
        var renderedElement;
        
        '
            判断 Component(elemen.type是一个函数)是否有 prototype属性。
        '
        var canInstantiate = ('prototype' in Component);
        
        if (canInstantiate) {
            。。。
            '
                省略多余代码,核心是下边的代码,
                element.type是一个构造函数的情况,这里是new type
                的一个实例。
                
                type是一个函数,或者是type本身就是指代的组件,也就是我们自定义的组件,这个组件是继承自ReactCompnent。我们使用 es5的写法的话就是 函数的形式,es6的形式就是类了。
                
                简单的来说,inst 就是自定义的组件的 实例。看图一与图二。
            '
            inst = new Component(publicProps, publicContext, ReactUpdateQueue);
            。。。
        }
    }
    

图一: 定义了一个App组件。

图二: 图一的组件,生成的inst则是:

浅色的是 继承自ReactComponent的属性,深色的是定义组件的时候定义的属性。

图三: App组件的Element.type的源码表示

mountComponent 2

    mountComponent: function (rootID, transaction, context) {
        
        '
            紧接mountComponent 1
            如果canInstantiate为false,或者是inst为null,false,或者是inst为一个element,就执行下边的操作。
            生成 StatelessComponent 实例。
            
            这里要看一下下边的 说明 ReactElement.isValidElement:
            
            如下边所叙述的,在无状态箭头函数组件下,inst是一个Element。renderedElement 也就是一个Element了。
            而后,inst为new一个 StatelessComponent的实例。
        '
        
        if (!canInstantiate || inst === null || inst === false || ReactElement.isValidElement(inst)) {
            renderedElement = inst;
            inst = new StatelessComponent(Component);
        }
        
        '
            为 inst 添加 属性,这里比较重要的是 updater。
            比如在 ReactComponent中的setState方法使用了。
            
        '
        inst.props = publicProps;
        inst.context = publicContext;
        inst.refs = emptyObject;
        inst.updater = ReactUpdateQueue;
    
        
        '
            将inst 存储在 _instance 属性里。
        '
        this._instance = inst;
        
        '
            存储 实例与组件element的对应。
            也就是在 实例 inst上添加属性 _reactInternalInstance,且值为 当前的element对象。
        '
        ReactInstanceMap.set(inst, this);
    }
ReactElement.isValidElement

这个方法主要是用来判断给定的对象是合法的 Element。 核心源码如下:

    ReactElement.isValidElement = function (object) {
        return typeof object === 'object' && object !== null && object.$$typeof === REACT_ELEMENT_TYPE;
    };
    

REACT_ELEMENT_TYPE 是React为内部的 Element提供的一个标识,这个方法的另外一个作用是检测给定的对象是不是一个 Element。

这里说回inst。我们知道inst是Element.type new出来的一个实例。这个实例一般是如上边图一图二所示的那样,当然有一种特例。

Stateless Arrow function Component

如下所示,我们定义了一个无状态箭头函数组件。

    import React from "react";

    const Stateless = () => <div>Hello </div>;
    export default Stateless;
    

它的 Element.type如下,而一个App组件则如上图三所示。

    ƒ Stateless() {
	  return _react2.default.createElement(
	    "div",
	    null,
	    "Hello "
	  );
	}

如此,我们new 一个 Element.type的实例如下:

图四:

如图所示,这个inst本身是一个 Element。

StatelessComponent
    function StatelessComponent(Component) {}
    
    StatelessComponent.prototype.render = function () {
      var Component = ReactInstanceMap.get(this)._currentElement.type;
      return Component(this.props, this.context, this.updater);
    };
    

如上所示的StatelessComponent构造函数只是在原型对象上添加了render方法,别的什么也没做,而new出来的 StatelessComponent的属性则是在后续的代码中加入的。如下所示

    inst.props = publicProps;
    inst.context = publicContext;
    inst.refs = emptyObject;
    inst.updater = ReactUpdateQueue;
ReactInstanceMap.set
    set: function (key, value) {
        key._reactInternalInstance = value;
    }

mountComponent 3

    mountComponent: function (rootID, transaction, context) {
        
        '
            initialState 是 自定义的组件的 state
        '
        var initialState = inst.state;
        '
            如果自定义的组件中没有state,就将其初始化为null。initialState也置为null
        '
        if (initialState === undefined) {
          inst.state = initialState = null;
        }
      
        '
            为 _pendingStateQueue 赋值 null
            _pendingReplaceState和 _pendingForceUpdate 值保持不变。
        '
        this._pendingStateQueue = null;
        this._pendingReplaceState = false;
        this._pendingForceUpdate = false;
        
        
        '
            生命周期componentWillMount开始执行。
        '
        if (inst.componentWillMount) {
          inst.componentWillMount();
        '
            当在挂载的时候, 在方法 componentWillMount 中 调用setState 方法,
            将会重设this._pendingStateQueue的值, , 而不会触发重新渲染。
            
           关于 _pendingStateQueue请看下边的。
        '
          if (this._pendingStateQueue) {
            '
                如果_pendingStateQueue有值。调用 _processPendingState 方法来处理state,综合处理之后最终得到一个最后的state。
            '
            inst.state = this._processPendingState(inst.props, inst.context);
          }
        }
    
       '
            如果组件不是无状态组件,我们现在渲染。
            在上边我们说过,在无状态箭头函数组件的情况下,这个 renderedElement是有值的。而且值为一个Element。而在其他情况下,renderedElement的值为 undefined。
            这里的具体解析请往下看。
       '
        if (renderedElement === undefined) {
          renderedElement = this._renderValidatedComponent();------(1)
        }
        
        '
            首先这里 renderedElement 是一个Element。
            接下来,我们再将 这个 Element 再次调用 方法 _instantiateReactComponent 来初始化挂载实例,而后赋值给 this._renderedComponent属性。
            
            也就是一个自定义组件在初始化挂载实例(instantiateReactComponent方法)之后,会再次将其初始化挂载实例(instantiateReactComponent)。因为自定义组件浏览器是不认的。
            
        '
    
        this._renderedComponent = this._instantiateReactComponent(renderedElement);
        
        '
            调用 ReactReconciler.mountComponent 方法生成 markup。
            至于 该方法具体是如何执行的往下看。
        '
        var markup = ReactReconciler.mountComponent(this._renderedComponent, rootID, transaction, this._processChildContext(context));
        if (inst.componentDidMount) {
          transaction.getReactMountReady().enqueue(inst.componentDidMount, inst);
        }
    
        return markup;
    }

_pendingStateQueue

_pendingStateQueue 属性是自定义组件初始化的挂载实例的一个属性,默认值是null。顾名思义,这个属性的作用是记录正在等待的 state队列,也就是我们如果一次性的操作了很多次的state,那么这里state就会形成一个 Queue,而后调用 _processPendingState 方法来处理。

也就是我们说的 setstate 的批处理。 我们来看一下_pendingStateQueue属性是在哪里发生改变的。

核心是在ReactComponent 模块的setState方法中。

    '
        updater 依据上文,我们可以知道它是 ReactUpdateQueue。
    '
    this.updater.enqueueSetState(this, partialState);

看一下 ReactUpdateQueue.enqueueSetState

    '
        拿到 _pendingStateQueue,并为其加入新元素。
        所以 _pendingStateQueue 就有了值。
    '
    var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []);
    queue.push(partialState);
_processPendingState
    _processPendingState: function (props, context) {
        var inst = this._instance;
        '
            转存一下 _pendingStateQueue的值。_pendingStateQueue 简单的来说就等待更新的state 暂时存在的地方。
        '
        var queue = this._pendingStateQueue;
        var replace = this._pendingReplaceState;
        this._pendingReplaceState = false;
        this._pendingStateQueue = null;
        
        '
            如果不存在 queue 直接返回 inst的 state,也就是组件实例的 state。
        '
        if (!queue) {
          return inst.state;
        }
        
        if (replace && queue.length === 1) {
          return queue[0];
        }
    
        var nextState = assign({}, replace ? queue[0] : inst.state);
        for (var i = replace ? 1 : 0; i < queue.length; i++) {
          var partial = queue[i];
          assign(nextState, typeof partial === 'function' ? partial.call(inst, nextState, props, context) : partial);
        }
        
        '
            这里就是叠加的一个过程,如果一个state已经存在了,后续的同名属性会覆盖前边的,直到最后一个为止。这也就是为什么有时候我们更改了n多次的state,最终反映在页面上只会有最后一次的变动。
        '
    
        return nextState;
      },
    
renderedElement-----(1)
    if (renderedElement === undefined) {
          renderedElement = this._renderValidatedComponent();------(1)
    }

首先我们得来先确认一下这个this是什么鬼。

我们知道,当前的模块是,在自定义组件的实例化挂载组件的,也就是一个构造函数,那么this当然是指向构造函数的实例了。

按照图一的例子,一个App组件,经过该构造函数构造的实例如下图五所示:

图五:

简单的来说,其实就是为实例加载了很多的方法,私有的(方法名以下划线开头的)和对外暴露的方法(比如mountComponent方法)。

而_renderValidatedComponent则是实例方法之一。

_renderValidatedComponent
    _renderValidatedComponent: function () {
        var renderedComponent;
        '
            简单的记录一下当前正在构建的组件
        '
        ReactCurrentOwner.current = this;
        try {
          renderedComponent = this._renderValidatedComponentWithoutOwnerOrContext();
        } finally {
          ReactCurrentOwner.current = null;
        }
        
        return renderedComponent;
    },
    
_renderValidatedComponentWithoutOwnerOrContext
    _renderValidatedComponentWithoutOwnerOrContext: function () {
        '
            _instance属性里记录了当前组件实例的inst,而一个inst就是一个Element.type方法的构造实例。
        '
        var inst = this._instance;
        '
            调用inst的render方法。也就是我们在组件中定义的render方法。render方法会一般会返回一个 Element,这里之所以说一般是因为有其他的情况,下边render有链 接,之前的文章有写,这个Element里包含了render方法里的jsx。
            比如图一的App组件的renderedComponent如下图 ,图六所示。
        '
        var renderedComponent = inst.render();
        
        return renderedComponent;
    },
renderedComponent

render

不同的组件render是不同的。简单的来说有三种render。参考文章这里就不再赘述了。

ReactReconciler.mountComponent
    mountComponent: function (internalInstance, rootID, transaction, context) {
        '
            这里又调用了 internalInstance 的 mountComponent 方法。
            internalInstance就是自定义组件初始化的挂载实例,这里相当于是递归了。一个自定义的组件终究是要实例化为一个 ReactDOMComponent或者是ReactTextComponent。
            
        '
        var markup = internalInstance.mountComponent(rootID, transaction, context);
        if (internalInstance._currentElement && internalInstance._currentElement.ref != null) {
          transaction.getReactMountReady().enqueue(attachRefs, internalInstance);
        }
        
        '
            总之,到了最后,mountComponent方法生成了一个 markup。
            markup就是组件的html的表示方法
            比如:
            <div data-reactid=".0">
                <span data-reactid=".0.0">this is app </span>
                <span data-reactid=".0.1">13</span>
                <div data-reactid=".0.2">Hello </div>
            </div>
        '
        return markup;
    },