Virtual DOM
是一棵以JavaScript
对象作为基础的树,每一个节点称为VNode
,用对象属性来描述节点,实际上它是一层对真实DOM
的抽象,最终可以通过渲染操作使这棵树映射到真实环境上,简单来说Virtual DOM
就是一个Js
对象,用以描述整个文档。
在浏览器中构建页面时需要使用DOM
节点描述整个文档。
如果使用Js
对象去描述上述的节点以及文档,那么便类似于下面的样子,当然这不是Vue
中用以描述节点的对象,Vue
中用以描述一个节点的对象包括大量属性,例如tag
、data
、children
、text
、elm
、ns
、context
、key
、componentOptions
、componentInstance
、parent
、raw
、isStatic
、isRootInsert
、isComment
、isCloned
等等,具体的属性可以参阅Vue
源码的/dev/src/core/vdom/vnode.js
。
在Vue
中首先会解析template
中定义的HTML
节点以及组件节点,为render
作准备,在解析的过程中会生成_c()
、_v()
等函数,其作为renderHelpers
用以创建节点,_v()
函数就是用以创建文本节点,而_c()
函数就是用以创建VNode
节点的,这个函数其实就是Vue
中定义的_createElement()
函数,通过这个函数来确定创建的是普通节点还是组件节点,具体可以在Vue
源码中/dev/src/core/vdom/create-element.js
以及/dev/src/core/vdom/create-element.js
查阅,当解析完成之后,便能够生成render
函数,而当render
函数执行后便返回了VNode
节点组成的虚拟DOM
树,树中的每一颗节点都会存储渲染的时候需要的信息,之后便是通过diff
算法以及patch
过程的createElm
或patchVnode
渲染到真实DOM
。
渲染真实DOM
的过程中开销是很大的,例如当有时候修改了某个数据或者属性,如果直接渲染到真实DOM
上可能会引起整个DOM
树的重绘与回流,而diff
算法能够只更新修改的那部分DOM
结构而不更新整个DOM
,这里需要说明的是操作DOM
结构的速度并不慢,而性能消耗主要是在浏览器重绘与回流的操作上。
当选用diff
算法进行部分更新的时候就需要比较旧DOM
结构与新DOM
结构的不同,此时就需要VNode
来描述整个DOM
结构,首先根据真实DOM
生成Virtual DOM
,当Virtual DOM
某个节点的数据改变后会生成一个新的Vnode
,然后通过newVNode
和oldVNode
进行对比,发现有不同之处便进行patch
修改于真实DOM
,然后使旧的Virtual DOM
赋值为新的Virtual DOM
。
简单来说建立Virtual DOM
的目的是减少对于整个DOM
的操作,通过建立Virtual DOM
来追踪如何改变真实DOM
,从而实现更高效地更新节点。
使用Virtual DOM
同样也是有部分缺点,代码更多,体积更大,内存占用增大,对于小量的单一的DOM
修改使用虚拟DOM
成本反而更高,但是整体来说,使用Virtual DOM
是优点远大于缺点的。