vue中虚拟DOM

644 阅读5分钟

这是我参与8月更文挑战的第15天,活动详情查看:8月更文挑战

什么是DOM?

DOM(Document Object Model文档对象模型) 是 W3C(万维网联盟)定义的访问 HTML 和 XML 文档的标准。

其中,HTML DOM 是关于如何获取、修改、添加或删除 HTML 元素的标准。

在 HTML DOM 中,所有事物都是节点。DOM 是被视为节点树的 HTML。

通过 HTML DOM,树中的所有节点均可通过 JavaScript 进行访问。所有 HTML 元素(节点)均可被修改,也可以创建或删除节点。

DOMtree.png

vue 模板转换成视图的过程

  • Vue.js通过编译将template 模板转换成渲染函数(render ) ,执行渲染函数就可以得到一个虚拟节点树
  • 在对 Model 进行操作的时候,会触发对应 Dep 中的 Watcher 对象。Watcher 对象会调用对应的 update 来修改视图。这个过程主要是将新旧虚拟节点进行差异对比,然后根据对比结果进行DOM操作来更新视图。

简单点讲,在Vue的底层实现上,Vue将模板编译成虚拟DOM渲染函数。结合Vue自带的响应系统,在状态改变时,Vue能够智能地计算出重新渲染组件的最小代价并应到DOM操作上。

模板转换成视图的过程.png

对图上的概念简单理解:

  • 渲染函数:用来生成虚拟DOM。
  • Vnode虚拟节点:它可以代表一个真实的 dom 节点。通过 createElement 方法能将 VNode 渲染成 dom 节点。简单地说,vnode可以理解成节点描述对象,它描述了应该怎样去创建真实的DOM节点。
  • patch(也叫做patching算法) :虚拟DOM最核心的部分,它可以将vnode渲染成真实的DOM,这个过程是对比新旧虚拟节点之间有哪些不同,然后根据对比结果找出需要更新的的节点进行更新。这点我们从单词含义就可以看出, patch本身就有补丁、修补的意思,其实际作用是在现有DOM上进行修改来实现更新视图的目的。Vue的Virtual DOM Patching算法是基于**Snabbdom**的实现,并在些基础上作了很多的调整和改进。

为什么需要虚拟DOM?

DOM元素的属性是十分庞大的,因为浏览器的标准就把 DOM 设计的非常复杂。当我们频繁的去做 DOM 更新,会产生一定的性能问题。而 Virtual DOM 就是用一个原生的 JS 对象去描述一个 DOM 节点,所以它比创建一个 DOM 的代价要小很多。

上面这种说法其实是不严谨的,以虚拟DOM比真实DOM快这句话其实是错的,或者说是不严谨的。那正确的说法是什么呢?虚拟DOM算法操作真实DOM,效率高于直接操作真实DOM虚拟DOM虚拟DOM算法是两种概念。虚拟DOM算法 = 虚拟DOM + Diff算法

  • 具备跨平台的优势

    由于 Virtual DOM 依赖于 JavaScript 对象,而不依赖真实平台环境。所以虚拟DOM具有跨平台的能力。

  • 直接操作 DOM 速度慢,将DOM对比操作放在JS层,可以提高效率。

    虚拟DOM算法操作真实DOM,效率高于直接操作真实DOM,运用patching算法来计算出真正需要更新的节点,最大限度地减少DOM操作,从而显著提高效率。

    Virtual DOM 本质上就是在 JS 和 DOM 之间做了一个缓存。

  • 提升渲染性能

    Virtual DOM的优势不在于单次的操作,而是在大量、频繁的数据更新下,能够对视图进行合理、高效的更新。

什么是虚拟DOM?

Virtual dom, 即虚拟DOM节点。它通过JS的Object对象模拟DOM中的节点,然后再通过特定的render方法将其渲染成真实的DOM节点。

所以就变成 代码 => Virtual DOM( 一个特殊的js对象) => DOM

Virtual DOM 其实就是一棵以 JavaScript 对象( VNode 节点)作为基础的树,用对象属性来描述节点,实际上它只是一层对真实 DOM 的抽象。最终可以通过一系列操作使这棵树映射到真实环境上。

所以说,虚拟DOM就是一个特殊的js对象,包含一些属性来定义一个虚拟DOM,比如最少包含tag props children等。

下面是Vue中对Vnode的定义:

export 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
    this.children = children
    this.text = text
    this.elm = elm
    this.ns = undefined
    this.context = context
    this.functionalContext = undefined
    this.key = data && data.key
    this.componentOptions = componentOptions
    this.componentInstance = undefined
    this.parent = undefined
    this.raw = false
    this.isStatic = false
    this.isRootInsert = true
    this.isComment = false
    this.isCloned = false
    this.isOnce = false
    this.asyncFactory = asyncFactory
    this.asyncMeta = undefined
    this.isAsyncPlaceholder = false
  }
}

用以上属性就可以表示一个DOM节点。

在vue中,模板转换成视图的过程为:模板 → 渲染函数 → 虚拟DOM树 → 真实DOM

模板。渲染函数。虚拟DOM、真实DOM.png

虚拟DOM的作用

虚拟DOM在Vue.js主要做了两件事:

  • 提供与真实DOM节点所对应的虚拟节点vnode
  • 将虚拟节点vnode和旧虚拟节点oldVnode进行对比,然后更新视图

虚拟DOM的最终目标是将虚拟节点渲染到视图上。但是如果直接使用虚拟节点覆盖旧节点的话,会有很多不必要的DOM操作。例如,一个ul标签下很多个li标签,其中只有一个li有变化,这种情况下如果使用新的ul去替代旧的ul,因为这些不必要的DOM操作而造成了性能上的浪费。

为了避免不必要的DOM操作,虚拟DOM在虚拟节点映射到视图的过程中,将虚拟节点与上一次渲染视图所使用的旧虚拟节点(oldVnode)做对比,找出真正需要更新的节点来进行DOM操作,从而避免操作其他无需改动的DOM。

参考

链接:juejin.cn/post/684490…

链接:www.cnblogs.com/fundebug/p/…

juejin.cn/post/698942…

juejin.cn/post/699495…