VUE中的虚拟DOM
1. 首先先讲一下什么是虚拟DOM,我们把组成一个DOM节点的必要的东西通过一个JS对象表示出来,那么这个JS对象就可以用来描述这个DOM对象,我们把这个JS对象称为这个DOM对象的虚拟DOM节点.
2. 为什么要有虚拟DOM:直接操作真实DOM是非常消耗性能的,为了尽可能的在更新视图的时候减少DOM操作,我们可以通过JS的计算性能来判断数据变化前后的状态,只更新那些需要更新的部分。
3. VUE中的虚拟DOM是怎么实现的:
3.1.VNode类 :在VUE中存在一个VNode类,通过这个类,我们可以实例化出不同类型的虚拟DOM节点:
// 源码位置:src/core/vdom/vnode.js
export default class VNode {
constructor (
tag?: string,
data?: VNodeData,
children?: ?Array<VNode>,
text?: string,
elm?: Node,
context?: Component,
componentOptions?: VNodeComponentOptions,
asyncFactory?: Function
) {
this.tag = tag /*当前节点的标签名*/
this.data = data /*当前节点对应的对象,包含了具体的一些数据信息,是一个VNodeData类型,可以参考VNodeData类型中的数据信息*/
this.children = children /*当前节点的子节点,是一个数组*/
this.text = text /*当前节点的文本*/
this.elm = elm /*当前虚拟节点对应的真实dom节点*/
this.ns = undefined /*当前节点的名字空间*/
this.context = context /*当前组件节点对应的Vue实例*/
this.fnContext = undefined /*函数式组件对应的Vue实例*/
this.fnOptions = undefined
this.fnScopeId = undefined
this.key = data && data.key /*节点的key属性,被当作节点的标志,用以优化*/
this.componentOptions = componentOptions /*组件的option选项*/
this.componentInstance = undefined /*当前节点对应的组件的实例*/
this.parent = undefined /*当前节点的父节点*/
this.raw = false /*简而言之就是是否为原生HTML或只是普通文本,innerHTML的时候为true,textContent的时候为false*/
this.isStatic = false /*静态节点标志*/
this.isRootInsert = true /*是否作为跟节点插入*/
this.isComment = false /*是否为注释节点*/
this.isCloned = false /*是否为克隆节点*/
this.isOnce = false /*是否有v-once指令*/
this.asyncFactory = asyncFactory
this.asyncMeta = undefined
this.isAsyncPlaceholder = false
}
get child (): Component | void {
return this.componentInstance
}
}
3.2:通过属性之间不同的搭配,VNode类可以描述出各种类型的真实DOM节点,再源码里:可以描述出以下几种类型的节点
○ 注释节点
○ 文本节点
○ 克隆节点
○ 元素节点
○ 组件节点
函数式组件节点
3.2.1:注释节点 :只需要两个属性,注释的文本text属性以及注释节点isComment属性
// 创建注释节点
export const createEmptyVNode = (text: string = '') => {
const node = new VNode()
node.text = text
node.isComment = true
return node
}
3.2.2:文本节点 :只需要一个文本text属性
// 创建文本节点
export function createTextVNode (val: string | number) {
return new VNode(undefined, undefined, undefined, String(val))
}
3.2.3:克隆节点 :克隆节点就是把已有节点的属性全部复制到新节点中去,其中,克隆节点中的isCloud为true
// 创建克隆节点
export function cloneVNode (vnode: VNode): VNode {
const cloned = new VNode(
vnode.tag,
vnode.data,
vnode.children,
vnode.text,
vnode.elm,
vnode.context,
vnode.componentOptions,
vnode.asyncFactory
)
cloned.ns = vnode.ns
cloned.isStatic = vnode.isStatic
cloned.key = vnode.key
cloned.isComment = vnode.isComment
cloned.fnContext = vnode.fnContext
cloned.fnOptions = vnode.fnOptions
cloned.fnScopeId = vnode.fnScopeId
cloned.asyncMeta = vnode.asyncMeta
cloned.isCloned = true
return cloned
}
3.2.4:元素节点 :元素节点更贴近于真实DOM节点,它有描述节点标签名词的tag属性,描述节点属性像class,attributes等的data属性,还有包含子节点信息的children属性等,由于所包含的情况相比很复杂,因此源码中也不能写死。下面举例子看一下
// 真实DOM节点
<div id='a'><span>VUE源码</span></div>
// VNode节点
{
tag:'div',
data:{},
children:[
{
tag:'span',
text:'VUE源码'
}
]
}