【Vue深入】之虚拟DOM

442 阅读5分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

vue深入系列包括以下内容,有兴趣的读者可以选择阅读:

【Vue深入】之DIFF算法 - 掘金 (juejin.cn)

【Vue深入】之生命周期 - 掘金 (juejin.cn)

【Vue深入】之路由router - 掘金 (juejin.cn)

【Vue深入】之响应式原理 - 掘金 (juejin.cn)

【Vue深入】之Vuex状态管理 - 掘金 (juejin.cn)

引言

现如今Vue已成为当下主流框架,其中一些核心概念更是面试须知,本文主要进行对Virtual DOM的一些原理分析,希望能够对大家有所帮助。

Vue.js 中,Virtual DOM 是用 VNode 这个 Class 去描述,它定义在 src/core/vdom/vnode.js 中。实际上 Vue.jsVirtual DOM 是借鉴了开源库  snabbdom 的实现,然后加入了一些 Vue.js 自己的一些特性。

1、真实DOM

在开始虚拟DOM之前,我们先要了解真实 DOM 的解析过程,通过介绍其解析过程以及存在的问题,从而引出为什么需要虚拟DOM

浏览器渲染引擎工作流程大致分为以下四步:创建DOM树 -> 生成Style Rules -> 生成render树 -> 布局render树 -> 绘制render树

  1. 创建DOM树:渲染引擎首先解析HTML文档,生成DOM树。 用CSS分析器,分析CSS文件和元素上的inline样式,生成页面的样式表。

  2. 生成样式表:用CSS分析器,分析CSS文件和元素上的inline样式,生成页面的样式表。

  3. 生成render树:将DOM树和样式表,关联起来,构建一颗Render(渲染)树。

  4. 布局render树:有了Render树,浏览器开始对渲染树的每个节点进行布局处理,确定其在屏幕上的显示位置。

  5. 绘制render树:遍历渲染树,然后调用每个节点的 paint 方法,将它们绘制出来。

    alt

2.虚拟DOM

什么是虚拟DOM

虚拟DOM(Virtual   Dom),也就是我们常说的虚拟节点,是用JS对象来模拟真实DOM中的节点,该对象包含了真实DOM的结构及其属性,用于对比虚拟DOM和真实DOM的差异,从而进行局部渲染来达到优化性能的目的。

为什么使用虚拟DOM

起初我们在使用JS进行DOM操作的的时候,DOM的变化会引发回流或重绘,从而降低页面渲染性能。所以虚拟DOM出现的主要目的就是为了减少频繁操作DOM而引起回流重绘所引发的性能问题。

虚拟DOM的作用

  1. 兼容性好。因为Vnode本质是JS对象,所以不管Node还是浏览器环境,都可以操作;
  2. 减少了对Dom的操作。页面中的数据和状态变化,都通过Vnode对比,只需要在比对完之后更新DOM,不需要频繁操作,提高了页面性能;

真实节点如何转化为虚拟DOM

在一个组件实例第一次被渲染的时候,它会先生成虚拟DOM树,然后根据虚拟DOM树创建真实DOM,最后会把真实的DOM挂载到页面中合适的位置。

如果说页面只需要渲染一次,后面的数据变化都不重新渲染页面,这时vue的效率跟直接操作DOM效率相比其实是更加低的,因为它比直接操作DOM还多一个步骤:创建虚拟DOM。所以说,页面初始化的时候创建真实的DOM这一步是少不了的,也就是大家常说的页面初始化的时候vue渲染效率是不高的。

但是当一个组件受到响应式数据变化的影响,需要重新渲染的时候,它便会重新调用render函数,创建一个新的虚拟DOM树,然后会使用新虚拟DOM树(newVnode)和旧虚拟dom树(oldVnode)进行对比,通过比对,vue会找到最小更新量,然后更新必要的虚拟DOM节点,最后,这些更新过的虚拟节点会去修改它们对应的真实DOM。这样一来就保证了对真实DOM的操作达到了最小的改动。

真实DOM

<ul id="list">
    <li class="item1">测试1</li>
    <li class="item2">测试2</li>
    <li class="item3">测试3</li>
</ul>

对应的虚拟DOM

let oldVDOM = {
        // 标签名
        tagName: 'ul',
        // 标签属性
        props: { 
            id: 'list'
        },
        // 标签子节点
        children: [
            {
                tagName: 'li', props: { class: 'item1' }, children: ['测试1']
            },
            {
                tagName: 'li', props: { class: 'item2' }, children: ['测试2']
            },
            {
                tagName: 'li', props: { class: 'item3' }, children: ['测试3']
            },
        ]
    }

3.虚拟DOM和真实DOM的区别

  • 虚拟DOM不会进行回流和重绘;
  • 真实DOM在频繁操作时引发的回流重绘导致性能很低;
  • 虚拟DOM频繁修改,然后一次性对比差异并修改真实DOM,最后进行依次回流重绘,减少了真实DOM中多次回流重绘引起的性能损耗;
  • 虚拟DOM有效降低大面积的重绘与排版,因为是和真实DOM对比,更新差异部分,所以只渲染局部;

计算使用真实DOM的损耗 总损耗 = 真实DOM增删改 + (多节点)回流/重绘;  

计算使用虚拟DOM的损耗 总损耗 = 虚拟DOM增删改 + (diff对比)真实DOM差异化增删改 + (较少节点)回流/重绘; 

可以发现,虚拟DOM对比真实DOM来说,减少了重绘和回流的次数,从而达到了减少性能消耗的目的。

结语

本文为笔者个人学习笔记,如有谬误,还请告知,万分感谢!如果本文对你有所帮助,还请点个关注点个赞~,您的支持是笔者不断更新的动力。