前言
Virtual Dom实际上是在浏览器端用JavaScript实现的DOM API,它之与React就类似一个虚拟空间,包括一套整体的virtual DOM模型,生命周期的维护和管理,性能高效的diff算法和将virtual DOM展示为原生DOM的Patch方法。
基于React开发的适合,所有DOM树都是通过virtual DOM构造的,React在virtual DOM上实现了DOM diff算法,当数据更新时,会根据diff算法寻找到变更的DOM节点,并只对变化的部分进行实际的浏览器的DOM更新,而不是重新渲染整个DOM树。
React也可以实现virtual DOM的批处理更新,当操作virtual DOM时,不会马上生成真实DOM,而是会将一个事件循环(event loop)内的两次数据更新进行合并,这样使得React能够在事件循环结束之前完全不用操作真实的DOM。
尽管每次都要构造完整的virtual DOM树,但由于virtual DOM是JavaScript对象,性能极高,而对原生DOM进行操作的仅仅是diff部分,因而能达到提高性能的目的。这样,在保证性能的同时,开发者不再需要关注某个数据的变化如何更新到具体的DOM原生,而只需要关系数据变化的情况下,整个界面是如何渲染的。
以上总结:
- Virtual DOM是JavaScript对象,
- Virtual DOM包括生命周期管理,diff算法,patch方法;
- Virtual DOM可实现批量更新;
- Virtual DOM性能搞,原生dom的操作集中在diff部分;
Virtual DOM模型
Virtual DOM之余React类似一个虚拟空间,React的所有工作都是基于Virtual DOM。Virtual DOM拥有一整套Virtual DOM标签,并负责虚拟节点和属性的构建、更新、删除等工作。
构建一套Virtual DOM模型,需要具备一个DOM标签所需的基本元素即可:
- 标签名
- 节点属性: 包含样式、属性、事件等
- 子节点
- 标识id
demo:
{
//标签名
tagName: 'div',
//属性
properties: {
style: {}
},
//子节点
children: [],
//唯一标识
key: 1
}
实际上Virtual DOM不止于此,但是也离不开这些基础元素。接下来深入了解下Virtual的DOM组成吧。
Virtual DOM的中节点称为ReactNode,有三种类型ReactElement、ReactFragment、ReactText。
其中ReactElement又分为ReactComponentElement和ReactDOMElement。
以下是ReactNode中不同类型节点所需的基础元素:
type ReactNode = ReactElement | ReactFragment | ReacText;
type ReactElement = ReactComponentElement | ReactDOMElement;
type ReactComponentElement<TProps> = {
type: ReactClass<TProps>,
props: TProps,
key: string | boolean | number | null,
ref: string | null
}
type ReactDOMElement = {
type:string,
props: {
children: ReactNodeList,
className: string,
etc.
},
key: string | boolean | number | null,
ref: string
}
type ReactFragment = Array<ReactNode | ReactEmpty>;
type ReactNodeList = ReactNode | ReactEmpty;
type ReactText = string | number;
type ReactEmpty = null | undefined | boolean;
创建React元素
我们都知道JSX语法,现在来回顾下,下面是一段JSX语法编译后的JavaScript:
const Nav, Profile;
//jsx
const app = <Nav color="blue"><Profile>Click</Profile></Nav>
//输出
const app = React.createElement({
Nav,
{color: blue},
React.createElement({
Profile,
null,
"click"
})
})
通过JSX创建的虚拟元素最终会被编译成调用React的createElement方法,那么createElement方法到底做了什么, 接下来看源码啦!
//createElement 只是做了简单的参数修正, 返回一个ReactElement实例对象。也就是virtual DOM的实例
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(propsName in defaultProps) {
if(typeof props[propName] === 'undefined') {
props[propName] = defaultProps[propName]
}
}
}
//返回一个reactElement实例对象
return ReactElement(type, key, ref, self, source, ReactCurrentOwner.current, props)
}
Virtual DOM模型通过createElement创建虚拟元素,那又是如何创建组件呢?下次再说~~