vue的虚拟DOM以及Diff算法

1.什么是虚拟DOM

用JS按照DOM结构来实现的树形结构对象,包含了tag、props、children三个属性

  • tag: 指定元素的标签类型,如’li’, ‘div’, 'a’等
  • props: 表示指定元素身上的属性,如class, style, 自定义属性等
  • children: 表示指定元素是否有子节点,参数以数组的形式传入
// HTML
<div id="app">
  <p class="text">hello world!</p>
</div>


// 上面的 HTML 转换成虚拟DOM 如下:
{
  tag: 'div',
  props: {
    id: 'app'
  },
  chidren: [
    {
      tag: 'p',
      props: {
        className: 'text'
      },
      chidren: [
        'hello world!'
      ]
    }
  ]
}

2.为什么需要虚拟DOM

既然已经有了DOM,为什么还需要额外加一层抽象?

  1. 为了尽可能少的操作DOM,不仅仅是DOM相对较慢,更因为频繁操作DOM会造成浏览器的重绘或者回流,这些都是性能的杀手。因此,我们需要这一层抽象,在patch过程中尽可能的一次性将差异更新到DOM中。
  2. 虚拟DOM最初的目的,就是更好的跨平台,比如Node.js就没有DOM,如果想实现SSR(服务端渲染),那么一个方式就是借助Virtual DOM,因为Virtual DOM本身是JavaScript对象.

3.整个DOM-diff的过程(vue的渲染过程)

  1. 用js对象模拟DOM(虚拟DOM)
    利用 createElement 方法创建 VNode,每个 VNode 有 children,children 每个元素也是一个 VNode,这样就形成了一个 VNode Tree,它很好的描述了我们的 DOM Tree

  2. 把此虚拟DOM转成真实DOM并插入页面中(render)

  3. 如果有事件发生修改了虚拟DOM,比较两颗虚拟DOM树的差异,得到差异对象(diff)

  4. 把差异对象应用到真正的DOM树上(patch)

4.虚拟DOM的Diff

  1. 当数据发生变化时,vue是怎么更新节点的
    先根据真实DOM生成一颗virtual DOM,当virtual DOM某个节点的数据改变后会生成一个新的Vnode,然后Vnode和oldVnode做对比,发现有不一样的地方就直接修改在真实的DOM上,然后使oldVnode的值为Vnode。
    diff的过程就是调用名为patch的函数,比较新旧节点,一边比较一边给真实的DOM打补丁。

  2. diff的比较方式
    在采取diff算法比较新旧节点的时候,比较只会在同层级进行, 不会跨层级比较。

  3. 流程
    当数据发生改变时,set方法会让调用Dep.notify通知所有订阅者Watcher,订阅者就会调用patch给真实的DOM打补丁,更新相应的视图。
    在这里插入图片描述

  4. 比较的过程

我们可以假设有oldVnode和Vnode这两个数组,而且有四个变量充当指针分别指到两个数组的头尾.。重复下面的对比过程,直到两个数组中任一数组的头指针超过尾指针,循环结束:

  • 头头对比: 对比两个数组的头部,如果找到,把新节点patch到旧节点,头指针后移

  • 尾尾对比: 对比两个数组的尾部,如果找到,把新节点patch到旧节点,尾指针前移

  • 旧尾新头对比: 交叉对比,旧尾新头,如果找到,把新节点patch到旧节点,旧尾指针前移,新头指针后移

  • 旧头新尾对比: 交叉对比,旧头新尾,如果找到,把新节点patch到旧节点,新尾指针前移,旧头指针后移

  • 利用key对比: 用新指针对应节点的key去旧数组寻找对应的节点,这里分三种情况,当没有对应的key,那么创建新的节点,如果有key并且是相同的节点,把新节点patch到旧节点,如果有key但是不是相同的节点,则创建新节点
    在这里插入图片描述
    在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/wytraining/article/details/110831015