一、Virtual DOM (虚拟DOM)
虚拟DOM就是React节点的对象树结构,React的工作原理就是基于虚拟DOM完成。这个对象树结构中每个节点的内容都包含了一个原生DOM所具有的属性。如:标签名称,属性,子节点,id等。如下这种模式:
{
tagName: '',
properties: {
},
children: [],
key: ''
}
复制代码
在React中虚拟DOM称为ReactNode。它有三种类型:ReactElement,ReactFragment,ReactText。ReactElement又分为ReactComponentElement,ReactDOMElement。
//ReactNode不用类型节点所需的基础元素
type ReactNode = ReactElement | ReactFragment | ReactText
type ReactElement = ReactDOMElement | ReactComponentElement
type ReactDOMElemnt = {
type: string,
props: {
children: ReactNodeList,
className: string,
style: object,
...etc
}
key: string | boolean | number | null,
ref: string | null
}
type ReactComponentElement<TProps> = {
type: ReactClass<Tprops>,
props: TProps,
key; string | boolean | number | null,
ref: string | null
}
type ReactFragment = Array<ReactNode | ReactEmpty>
type ReactNodeList = ReactNode | ReactEmpty
type ReactText = string | number
type ReactEmpty = null | undefined | boolean
复制代码
创建一个React元素
创建一个React元素,它调用的是React.createElement方法。那么这个方法做了什么? 看一下JSX和编译后的JavaScript文件:
const app = <Nav ref="nav" key="1" color="blue"><Profile>click</Profile>我是文本</Nav>;
const app = React.createElement(
Nav,
{
ref: 'nav',
key: 1,
color: 'blue'
},
React.createElement(Profile, {}, "click" )
)
复制代码
通过JSX创建的虚拟元素最终会编译成React的createElement方法。 源码目录在此:
//react中createElement方法来源于 ReactElement.js
//createElement类似一个工厂方法,只是做了简单的参数修正,返回一个ReactElement实例对象。
/**
* 传入了如下参数:
* type: "Nav"
* config: { ref: "nav", key: "1" }
* children: 1.react.createElement(...)
* 2.我是文本
*
*/
export function createElement(type, config, children) {
//可以看到这几个声明的变量就是一个初始化参数的作用
let propName;
const props = {};
let key = null;
let ref = null;
let self = null;
let source = null;
//这里判断如果config不为空的话则提取config上的内容,赋值给前面初始化的变量
if (config != null) {
if (hasValidRef(config)) {
ref = config.ref; //将config上的ref赋值给ref
}
if (hasValidKey(config)) {
key = '' + config.key; //将config上的key赋值给key
}
//将config上的self赋值给self
self = config.__self === undefined ? null : config.__self;
//将config上的source赋值给source
source = config.__source === undefined ? null : config.__source;
//下面的循环是将config上的内容复制到props上
for (propName in config) {
if (
hasOwnProperty.call(config, propName) &&
!RESERVED_PROPS.hasOwnProperty(propName)
) {
props[propName] = config[propName];
}
}
}
//这里是对children的操作,如果childrenLength为1,那么表示只有一个children,那么就直接赋值给props的children属性
//如果childrenLength大于1,则表示有多个children,那么要做合并操作,将他们放到一个数组里面。然后赋值给props的children属性
const childrenLength = arguments.length - 2;
if (childrenLength === 1) {
props.children = children;
} else if (childrenLength > 1) {
const childArray = Array(childrenLength);
for (let i = 0; i < childrenLength; i++) {
childArray[i] = arguments[i + 2];
}
props.children = childArray;
}
// Resolve default props
//这个type是创建的标签名称
//这里处理默认的props,如果存在默认的props,则将默认的props赋值给当前的props
if (type && type.defaultProps) {
const defaultProps = type.defaultProps;
for (propName in defaultProps) {
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
}
}
}
//最后返回一个ReactElement实例对象
return ReactElement(
type, //这个type代表的是组件名
key,
ref,
self,
source,
ReactCurrentOwner.current,
props,
);
}
复制代码
ReactCurrentOwner.current是个啥子东西?
const ReactCurrentOwner = {
/**
* @internal
* @type {ReactComponent}
*/
current: (null: null | Fiber),
currentDispatcher: (null: null | Dispatcher),
};
// 实际上这个current初始时是null,类型可以是Fiber或null
复制代码
最后返回的是一个ReactElement实例对象,那么这个个ReactElement主要做了些什么呢?
//简化一下,发现最后返回的是这个element对象。这就是最终的虚拟DOM
const ReactElement = function(type, key, ref, self, source, owner, props) {
const element = {
//这个标志表示这个元素是一个React Element
$$typeof: REACT_ELEMENT_TYPE,
//将属性注入到元素上
type: type,
key: key,
ref: ref,
props: props,
// 记录创建此元素的组建
_owner: owner,
};
return element;
};
复制代码
可以看到这个方法最后返回的是一个对象,这个对象结构对应着创建一个DOM的条件。这就是创建了一个ReactNode。发现也是很简单的吧。哈哈。
二、小知识点
在使用React创建组件之前我们还有一个操作,就是初始化组建入口。通过判断不同node类型来区分不同组件的入口。通过调用instantiateReactComponent方法进行初始化。
// Given a ReactNode, create an instance that will actually be mounted
// @param {ReactNode} node //参数是一个ReactNode
//参数是传一个虚拟DOM节点
function instantiateReactComponent(node) {
var instance;
//如果ReactNode是一个空组件
//通过ReactEmptyComponent.create初始化一个空组件
if (node === null || node === false) {
instance = ReactEmptyComponent.create(instantiateReactComponent);
//如果ReactNode是一个对象(DOM标签或者自定义组件)
} else if (typeof node === 'object') {
var element = node;
invariant(
element && (typeof element.type === 'function' ||
typeof element.type === 'string'),
'Element type is invalid: expected a string (for built-in components) ' +
'or a class/function (for composite components) but got: %s.%s',
element.type == null ? element.type : typeof element.type,
getDeclarationErrorAddendum(element._owner)
);
//如果element的type类型为string
//则调用ReactNativeComponent.createInternalComponent
if (typeof element.type === 'string') {
instance = ReactNativeComponent.createInternalComponent(element);
} else if (isInternalComponentType(element.type)) {
instance = new element.type(element);
//否则初始化自定义组件
} else {
instance = new ReactCompositeComponentWrapper(element);
}
//如果ReactNode是一个字符串或者数字
//那么调用ReactNativeComponent.createInstanceForText
} else if (typeof node === 'string' || typeof node === 'number') {
instance = ReactNativeComponent.createInstanceForText(node);
} else {
invariant(
false,
'Encountered invalid React node of type %s',
typeof node
);
}
instance._mountIndex = 0;
instance._mountImage = null;
return instance;
}
复制代码