Virtual DOM模型
1.Virtual DOM模型负责Virtual DOM底层框架的构建工作,它拥有一整套的Virtual DOM标签,
并负责虚拟节点及其属性的构建,更新,删除等工作.
2.其实,构建一套简易Virtual DOM模型并不复杂,它只需要具备一个DOM标签所需的基本元素即可.
{
// 标签名
tagName: 'div',
// 属性
properties: {
// 样式
style: {}
},
// 子节点
children: [],
// 唯一标识
key: 1
}
3.Virtual DOM中的节点称为ReactNode,它分为3种类型:ReactElement,ReactFragment,ReactText.
其中,ReactElement又分为ReactComponentElement和ReactDOMElement.
创建React元素
// 输入jsx
const app = <Nav color="blue"><Profile>click</Profile></Nav>;
// 输出js
const app = React.createElement(
Nav,
{color: 'blue'},
React.createElement(Profile, null, 'click')
);
通过jsx创建的虚拟元素最终会被编译成调用React的createElement方法
// createElement只是做了简单修正,返回一个ReactElement实例对象
// 也就是虚拟元素的实例
ReactElement.createElement = function(type, config, children) {
// 初始化参数
var propName;
var props = {};
var key = null;
var ref = null;
var self = null;
var source = null;
// 如果存在config,则提取里面的内容
if (config != null) {
ref = config.ref === undefined ? null : config.ref;
key = config.key === undefined ? null : '' + config.key;
self = config._self === undefined ? null : config._self;
source = config._source === undefined ? null : config._source;
// 复制config里的内容到props(id和className等)
for (propName in config) {
if (config.hasOwnProperty(propName) && !RESERVED_PROPS.hasOwnProperty(propName)) {
props[propName] = config[propName];
}
}
}
// 处理children,全部挂载到props的children属性上,如果只有一个参数,直接赋值给children
// 否则做合并处理
var childrenLength = arguments.length - 2;
if (childrenLength === 1) {
props.children = children;
} else if (childrenLength > 1) {
var childArray = Array(childrenLength);
for (var i = 0; i < childrenLength; i++) {
childArray[i] = arguments[i + 2];
}
props.children = childArray;
}
// 如果某个prop为空且存在默认的prop,则将默认prop赋给当前的prop
if (type && type.defaultProps) {
var defaultProps = type.defaultProps;
for (propName in defaultProps) {
if (typeof props[propName] === 'undefined') {
props[propName] = defaultProps[propName]
}
}
}
// 返回一个ReactElement实例对象
return ReactElement(type, key, ref, self, source, ReactCurrentOwner.current, props);
}
初始化组件入口
1.当使用React创建组件时,首先会调用instantiateReactComponent,这就是初始化组件的入口函数,
它通过判断node类型来区分不同组件的入口.
// 初始化组件入口
function instantiateReactComponent(node, parentCompositeType) {
var instance;
// 空组件 (ReactEmptyComponent)
if (node === null || node === false) {
instance = ReactEmptyComponent.create(instantiateReactComponent);
}
if (typeof node === 'object') {
var element = node;
if (typeof element.type === 'string') {
// DOM标签 (ReactDOMComponent)
instance = ReactNativeComponent.createInternalComponent(element);
} else if (isInternalComponentType(element.type)) {
// 不是字符串表示的自定义组件暂无法使用,此处将不做组件初始化操作
instance = new element.type(element);
} else {
// 自定义组件
instance = new ReactCompositeComponentWrapper();
}
} else if (typeof node === 'string' || typeof node === 'number') {
// 字符串或数字
instance = ReactNativeComponent.createInstanceForText(node);
} else {
// 不做处理
}
// 设置实例
instance.construct(node);
// 初始化参数
instance._mountIndex = 0;
instance._mountImage = null;
return instance;
}
文本组件
1.当node类型为文本节点时是不算Virtual DOM元素的,但React为了保持渲染的一致性,
将其封装为文本组件ReactDOMTextComponent.
DOM标签组件
1.Virtual DOM模型涵盖了几乎所有的原生DOM标签,如<div>,<p>,<span>等.
当开发者使用React时,此时的<div>并不是原生的<div>标签,他其实是React生成的
Virtual DOM对象,只不过标签名称相同罢了.
_createOpenTagMarkupAndPutListeners: function(transaction, props) {
var ret = '<' + this._currentElement.type;
// 拼凑出属性
for (var propKey in props) {
var propValue = props[propKey];
if (registrationNameModules.hasOwnProperty(propKey)) {
// 针对当前的节点添加事件代理
if (propValue) {
enqueuePutListener(this, propKey, propValue, transaction);
}
} else {
if (propKey === STYLE) {
if (propValue) {
// 合并样式
propValue = this._previousStyleCopy = Object.assign({}, props.style);
}
propValue = CSSPropertyOperations.createMarkupForStyles(propValue, this);
}
// 创建属性标识
var markup = null;
if (this._tag != null && isCustomComponent(this._tag, props)) {
markup = DOMPropertyOperations.createMarkupForProperty(propKey, propValue);
}
if (markup) {
ret += ' ' + markup;
}
}
}
// 对于静态页面,不需要设置react-id,这样可以节省大量字节
if (transaction.renderToStaticMarkup) {
return ret;
}
// 设置reactid
if (!this._nativeParent) {
ret += ' ' + DOMPropertyOperations.createMarkupForRoot();
}
ret += ' ' + DOMPropertyOperations.createMarkupForID(this._domID);
return ret;
}