一首歌的时间说说vue的虚拟dom

72 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 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值和位置关系来修改节点。