React@15.6.2源码解析---从 ReactDOM.render 到页面渲染(2)instantiateReactComponent

instantiateReactComponent 方法是React中的一个很重要的方法,主要作用是根据给定的node(ReactElement类型)对象,实例化出一个将被挂载的实例。实例化出的实例大体有三种,ReactEmptyComponent、ReactCompositeComponent、ReactHostComponent,其中ReactCompositeComponent是重点,内部包装了很多方法,而ReactHostComponent主要用来实例化文本组件实例。

概述
Given a ReactNode, create an instance that will actually be mounted.
给一个ReactNode 指的是 ReactElement 对象,根据该对象创建一个实际要挂载的实例

大致流程
判断 node 的类型以及 node.type 的类型,根据不同的类型创建不同的实例

node 为 null | false

if (node === null || node === false) {
    instance = ReactEmptyComponent.create(instantiateReactComponent);
  } else if (typeof node === 'object') {
    var element = node;
    var type = element.type; // function
    if (typeof type !== 'function' && typeof type !== 'string') {
      var info = '';
      if (process.env.NODE_ENV !== 'production') {
        /**/
      }
      info += getDeclarationErrorAddendum(element._owner); // ''
      !false ? /**/
    }

    // Special case string values
    if (typeof element.type === 'string') {
      instance = ReactHostComponent.createInternalComponent(element);
    } else if (isInternalComponentType(element.type)) {
      if (!instance.getHostNode) {
        instance.getHostNode = instance.getNativeNode;
      }
    } else {
      instance = new ReactCompositeComponentWrapper(element);
    }
  } else if (typeof node === 'string' || typeof node === 'number') {
    instance = ReactHostComponent.createInstanceForText(node);
  } else {
    !false ? /**/
  }

node 的类型是null或者为false,创建一个ReactEmptyComponent实例。

ReactEmptyComponent

// ReactEmptyComponent.js
var emptyComponentFactory;

var ReactEmptyComponentInjection = {
  injectEmptyComponentFactory: function (factory) {
    emptyComponentFactory = factory;
  }
};

var ReactEmptyComponent = {
  create: function (instantiate) {
    return emptyComponentFactory(instantiate);
  }
};

ReactEmptyComponent.injection = ReactEmptyComponentInjection;

module.exports = ReactEmptyComponent;

这边可以看出ReactEmptyComponent.create方法就是调用emptyComponentFactory()方法,而emptyComponentFactory是外部闭包的一个对象,是在全局依赖注入是调用ReactEmptyComponent.injection时进行注入的。注入的地方在ReactDefaultInjection.js文件,这也是上一篇博客的开头所阐述的全局依赖注入,作用就在于给一些函数外部闭包的变量对象某个属性进行赋值,这样做方便管理,神乎其技的封装。我觉得这种优秀的封装也是我们该学习的一部分。有助于我们写出更加完善的代码。

ReactInjection.EmptyComponent.injectEmptyComponentFactory(function (instantiate) {
    return new ReactDOMEmptyComponent(instantiate);
  });

那么这边给emptyComponentFactory赋值的是一个函数,这个函数返回一个new ReactDOMEmptyComponent(instantiate);ReactDOMEmptyComponent实例。

ReactDOMEmptyComponent实例

var ReactDOMEmptyComponent = function (instantiate) {
  // ReactCompositeComponent uses this:
  this._currentElement = null;
  // ReactDOMComponentTree uses these:
  this._hostNode = null;
  this._hostParent = null;
  this._hostContainerInfo = null;
  this._domID = 0;
};
_assign(ReactDOMEmptyComponent.prototype, {
  mountComponent: function (transaction, hostParent, hostContainerInfo, context) {
    /**/
    }
  },
  receiveComponent: function () {},
  getHostNode: function () {
    return ReactDOMComponentTree.getNodeFromInstance(this);
  },
  unmountComponent: function () {
    ReactDOMComponentTree.uncacheNode(this);
  }
});

这边我们大致了解一下有那些属性等用到时在做阐述。那么由此可以看出,ReactEmptyComponent创建的实例实质上就是ReactDOMEmptyComponent

node 为 object##

当 node 的类型为一个object时,这边大多数情况指的是我们的node是一个ReactElement。接下来会对node.type做判断。

node.type !== function || node.type !== string

type 既不是 function 也不是一个 string 那么会报一个警告,讲道理要么是一个构造函数例如我们的App,那么是一个字符串例如一个div标签,下面给一个例子

node一层层剥开,看他的child,剧透一下,后面一步步的忘深处创建实例,那么肯定会遇到一个type: 'div'的ReactElement,这个时候 type 就是string了

node.type === string

那么当node.type是一个字符串的时候,会创建一个ReactHostComponet实例,这边和文本节点有所区别,这边调用的是ReactHostComponet.createInternalComponent方法,文本节点的话会调用ReactHostComponet.createInstanceForText下方会遇到。

// ReactHostComponent.js
function createInternalComponent(element) {
  !genericComponentClass /**/
  return new genericComponentClass(element);
}

这个函数首先会判断genericComponentClass是否存在,而这个变量是函数外部闭包的一个变量,和之前一样是在全局依赖注入的时候完成了赋值。

var ReactHostComponentInjection = {
  // This accepts a class that receives the tag string. This is a catch all
  // that can render any kind of tag.
  injectGenericComponentClass: function (componentClass) {
    genericComponentClass = componentClass;
  },
  // This accepts a text component class that takes the text string to be
  // rendered as props.
  injectTextComponentClass: function (componentClass) {
    textComponentClass = componentClass;
  }
};

那么回到ReactDefaultInjection继续查找这边注入的是什么

ReactInjection.HostComponent.injectGenericComponentClass(ReactDOMComponent);

那么此时genericComponentClass就是这边传入的ReactDOMComponent,那么就是返回一个ReactDOMComponent实例

isInternalComponentType(element.type)

检测 node.type 是不是内部组件类型

function isInternalComponentType(type) {
  return typeof type === 'function' && typeof type.prototype !== 'undefined' && typeof type.prototype.mountComponent === 'function' && typeof type.prototype.receiveComponent === 'function';
}

如果是内部的类型的化,那么就直接实例化传入的node,具体例子我还没有遇到过,就不做过多理解了,有例子的同学欢迎指教。

node.type 不是上述类型

其实这边的意思就是 node.type 是一个function,因为 node.type 只能是 function 或者 string。只不过逃过了上述isInternalComponentType函数的检测。
那么这边是创建的一个ReactCompositeComponentWrapper实例,这个构造函数是React的一大重点,根据我的四级英语翻译为 ' React复合类型 ' 他的本质是ReactCompositeComponet内部实现了React的组件生命周期,挂载卸载等操作。具体的后面会有很大一部分讲解他。这也是代码量可以说最大的几个文件之一了。

// 这边是定义了一下这个构造函数,在该文件的最下面,对该构造函数的原型链添加了一系列的方法
var ReactCompositeComponentWrapper = function (element) {
  this.construct(element);
  // console.log(this);
};

_assign(ReactCompositeComponentWrapper.prototype, ReactCompositeComponent, {
  _instantiateReactComponent: instantiateReactComponent
});

这边可以看出ReactCompositeComponentWrapper的主要操作都在ReactCompositeComponent文件里,而这边覆盖了一个_instantiateReactComponent属性,值为当前讲解的这个创建实例的函数instantiateReactComponent

node 为 string | number

当 node 为 string 或者 number 时,表明这是一个文本组件,对调用ReactHostComponent.createInstanceForText,那么加上当 node.type 为 string 时的讲解,可以看出ReactHostComponent组件描述的都是React中的文本组件。而ReactHostComponent.createInstanceForText方法引用的是外部闭包的textComponentClass,一样也是在全局依赖注入的时候赋值的。

// ReactHostComponent.js
/**
 * @param {ReactText} text
 * @return {ReactComponent}
 */
function createInstanceForText(text) {
  return new textComponentClass(text);
}

// ReactDefaultInjection.js
ReactInjection.HostComponent.injectTextComponentClass(ReactDOMTextComponent);

那么本质就是创建一个 ReactDOMTextComponent 实例。

node 不是上述类型

这边不是上述类型,那么就报错了。

新增 _mountIndex _mountIage

在得到实例之后,会给instance添加两个额外的属性。根据官方给的英文注释,大致可以猜出这两个字段是用于 DOM 和 Diff 算法上的。React的Diff算法我也会写博客讲解,只不过得等这个渲染的一系列写完。React万岁!!!

 // These two fields are used by the DOM and ART diffing algorithms
  // respectively. Instead of using expandos on components, we should be
  // storing the state needed by the diffing algorithms elsewhere.
  // 这两个字段是 DOM 和 ART diff算法所需要的字段。我们应该在其他地方存储diff算法所需要的state,
  // 而不是在组件上扩展他
  // 这两个字段分别用于DOM和ART diffing算法。我们不应该在组件上使用expandos,而是应该存储其他不同算法所需的状态。
  instance._mountIndex = 0;
  instance._mountIage = null;

总结

instantiateReactComponent 函数总体来说还是很简单的,只是根据 node 的类型 和 node.type 的类型创建一个实例,这边的代码还是可以理解的,不了解实例内部的那些方法的话难度还是可以接受的,难的地方还在后面,那么对该函数做一个流程图如下

本片留坑

各个实例的内部属性、方法

坑总会填的,不急不急

下一篇讲解 ReactUpdates.js 这也是一个重点,博客是按照整个渲染的流程,部分重点文件会单独拉出来一篇进行理解。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容