虚拟DOM简介
所谓虚拟DOM,就是用一个JS
对象来描述一个DOM
节点,像如下示例:
<div class="a" id="b">我是内容</div>
{
tag:'div', // 元素标签
attrs:{ // 属性
class:'a',
id:'b'
},
text:'我是内容', // 文本内容
children:[] // 子元素
}
我们把组成一个DOM
节点的必要东西通过一个JS
对象表示出来,那么这个JS
对象就可以用来描述这个DOM
节点,我们把这个JS
对象就称为是这个真实DOM
节点的虚拟DOM
节点。
Vue中为什么要引入虚拟DOM
我们知道,Vue
是数据驱动视图的,数据发生变化视图就要随之更新,在更新视图的时候难免要操作DOM
,而操作真实DOM
又是非常耗费性能的,这是因为浏览器的标准就把 DOM
设计的非常复杂,所以一个真正的 DOM
元素是非常庞大的,如下所示:
let div = document.createElement('div')
let str = ''
for (const key in div) {
str += key + ''
}
console.log(str)
上图中我们打印一个简单的空div
标签,就打印出这么多东西,更不用说复杂的、深嵌套的DOM
节点了。由此可见,直接操作真实DOM
是非常消耗性能的。
那么有没有什么解决方案呢?当然是有的。我们可以用JS
的计算性能来换取操作DOM
所消耗的性能。
既然我们逃不掉操作DOM
这道坎,但是我们可以尽可能少的操作DOM
。那如何在更新视图的时候尽可能少的操作DOM
呢?最直观的思路就是我们不要盲目的去更新视图,而是通过对比数据变化前后的状态,计算出视图中哪些地方需要更新,只更新需要更新的地方,而不需要更新的地方则不需关心,这样我们就可以尽可能少的操作DOM
了。这也就是上面所说的用JS
的计算性能来换取操作DOM
的性能。
我们可以用JS
模拟出一个DOM
节点,称之为虚拟DOM
节点。当数据发生变化时,我们对比变化前后的虚拟DOM
节点,通过DOM-Diff
算法计算出需要更新的地方,然后去更新需要更新的视图。
这就是虚拟DOM
产生的原因以及最大的用途。
Vue中如何实现虚拟DOM
因为虚拟DOM
就是用JS
来描述一个真实的DOM
节点,所以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
}
}
从上面的代码中可以看出:VNode
类中包含了描述一个真实DOM
节点所需要的一系列属性,如tag
表示节点的标签名,text
表示节点中包含的文本,children
表示该节点包含的子节点等。通过属性之间不同的搭配,就可以描述出各种类型的真实DOM
节点。
通过本篇,我们了解了什么是虚拟DOM以及虚拟DOM的作用,并知道了Vue中是通过创建VNode
类实现虚拟DOM的。下一篇,我们将详细介绍VNode类是如何运作的。