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; }