携手创作,共同成长!这是我参与「掘金日新计划 · 8月更文挑战」的第10天,点击查看活动详情
什么是虚拟DOM?
虚拟DOM就是用js对象来描述一个DOM节点,这个JS对象就称为是这个真实DOM节点的虚拟DOM节点。
我们都知道,在vue中,数据发生变化,视图就要随之更新。而操作真实DOM是非常耗费性能的。所以我们通过js模拟一个DOM节点,数据变化后,对比变化前后的虚拟DOM节点,通过diff算法计算出需要更新的地方,然后去更新这些地方。大程度的提升了效率。
Vue中的虚拟DOM
VNode类
vue中,通过VNode类实例化出不同类型的虚拟DOM节点。VNode类中包含了描述真实DOM所需的一系列属性。如tag,text等。
// 源码位置: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对应的虚拟DOM节点。
vue中的dom-diff
DOM-Diff算法是整个虚拟DOM的核心所在,就是用它来对比前后两份虚拟DOM节点的差异。
dom-diff的过程叫做patch过程。patch需要做的事就是改造旧的VNode,使之和新的VNode一样。主要就是创建节点,删除节点,更新节点。
diff算法
同级比较,标签不一样直接替换节点。
标签一样时,先进行属性替换,然后对比子元素。
新的没子元素,老的有,直接清空innerHtml。新的有,老的没有,直接将子元素的虚拟节点转为真实节点插入。
新老都有子元素,则用双指针方式比对,比较tag和key,完全相同进行节点复用。具体鳔胶方法,头头比较,尾尾比较,头尾比较。如果都不符合,则先遍历旧子节点数组形成key值映射的map对象,然后根据新子节点的数组循环,按照key值和位置关系来修改节点。