VUE之虚拟DOM和diff算法

246 阅读3分钟
简单来讲,vue的虚拟dom就是一个JavaScript对象,用对象的方式去描述dom的结构。
<div class="a" id="b">我是内容</div>
 
{
      tag:'div',        // 元素标签
      attrs:{           // 属性
            class:'a',
            id:'b'
      },
      text:'我是内容',  // 文本内容
      children:[]       // 子元素
}
为什么要使用虚拟dom

我们知道浏览器将页面的dom设计的非常复杂,而vue的特点之一是数据驱动视图,换言之,也就是说,vue是通过数据的变化来驱动视图的更新,也就意味着是数据变化对dom的操作非常的频繁,这样的代价是非常大,但是我们又逃不多对dom的操作,那么怎么办呢?其实很简单,我们可以在数据变化前后生成2份dom,然后通过diff算法将这2份dom做比较,看看他们有什么不一样的,将不一样的dom更新到真实的dom上,是不是就大大就减少了真实的dom的操作,其实就是我们所说的虚拟dom。

vue如何实现虚拟dom

那么vue中是如何实现的虚拟dom呢?其实也很简单,就是通过vnode这样一个类实现,直接上源码:

// 源码位置: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属性。

VNode类型
  1. 注释节点
  2. 文本节点
  3. 元素节点
  4. 组件节点
  5. 函数式组件节点
  6. 克隆节点

Diff算法

提到dom,diff算法就是被紧紧的联系在一起,那么到底什么是diff算法呢?
在vue中把diff算法称之为patch的过程,patch的意思就是打补丁。
那么这个patch的过程是怎么样的呢?
我们知道:对于dom的操作无非就是增、删、改、查这四个字,那么vue的patch的过程其实就是VNode的增、删、改的过程。
所以首先要讲清楚patch的对象是什么?那就是,每次数据变化之前的虚拟VNode,和数据变化之后的虚拟VNode,以新的VNode为基准,改造旧的oldVNode使之成为跟新的VNode一样,这就是patch过程要干的事。\color{red}{每次数据变化之前的虚拟VNode,和数据变化之后的虚拟VNode,以新的VNode为基准,改造旧的oldVNode使之成为跟新的VNode一样,这就是patch过程要干的事。}

对操作前后的dom树同一层的节点进行对比,一层一层对比
使用key来给每个节点做唯一标识,diff算法就可以正确地识别此节点,找到正确地位置插入新的节点。

借鉴引用了大神的文章:blog.csdn.net/leelxp/arti…