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 这也是一个重点,博客是按照整个渲染的流程,部分重点文件会单独拉出来一篇进行理解。