# 简介

虚拟DOM(Virtual DOM)是近年来在前端开发中崭露头角的概念,它在提升性能、优化渲染和改善用户体验方面发挥了重要的作用。本章将深入探索虚拟DOM的工作原理和背后的思想。我们将介绍虚拟DOM是如何在Vue中发挥作用的。

# 什么是虚拟DOM

虚拟DOM是一个JavaScript对象的表示,它是对真实DOM的轻量级抽象。如下示例:

<div class="button" id="btn">这是一个按钮</div>

{
  tag:'div',        // 元素标签
  attrs:{           // 属性
    class:'button',
    id:'btn'
  },
  text:'这是一个按钮',  // 文本内容
  children:[]       // 子元素
}
成功
1
2
3
4
5
6
7
8
9
10
11

像这样,可以用一个JavaScript对象描述真实DOM节点的,我们称这个JavaScript对象真实DOM节点的虚拟DOM节点

# 为什么要用虚拟DOM

我们知道,想要在视图中渲染,势必就要操作真实DOM,而操作真实DOM又是非常耗费性能的。因为在浏览器标准中,真实DOM就是一个非常复杂的结构。我们可以尝试用以下方式打印真实DOM来查看

空div打印

可以看到创建一个空div的真实DOM都十分的庞大,更何况我们在复杂的应用中需要操作大量的真实DOM,还有较为复杂的结构,这势必会对性能造成一定的影响。

所以如何尽量真实DOM的操作,成了框架设计中一个避不开的环节。在这个基础上,虚拟DOM应运而生。

# 虚拟DOM如何节省性能

虚拟DOM通过在内存中构建一个轻量级的DOM树来解决这个问题。当应用程序的状态发生变化时,我们首先在虚拟DOM上进行修改,而不是直接操作真实DOM。通过比较新旧虚拟DOM的差异,我们可以确定哪些部分需要更新,并仅对这些部分进行实际的DOM操作。这种优化技术被称为"DOM Diffing"。

虚拟DOM的工作流程如下:

  1. 初始渲染:首次渲染时,将真实DOM转换为虚拟DOM。
  2. 状态变更:当应用程序的状态发生变化时,修改虚拟DOM树。
  3. 虚拟DOM Diffing:比较新旧虚拟DOM树的差异,找出需要更新的部分。
  4. 生成变更集:根据差异生成最小化的DOM操作序列。
  5. 应用变更:将变更应用于真实DOM,更新页面展示。

通过使用虚拟DOM,我们可以减少真实DOM操作的次数,并最大程度地减少重绘和回流的成本。这种优化能够显著提升性能,特别是在大型复杂的应用程序中,或是需要频繁更新的动态内容中。

# 虚拟DOM比真实DOM性能消耗更小么?

先说答案,是也不是

这是什么意思呢?

我们先假设一个场景,我需要用原生JavaScript更改div的内容,代码如下:

const div = document.getElementById('div');
div.textContent = 'changed';
成功
1
2

现在思考下,还有没有比这个性能更好的替代方案?

很显然,并没有。可以看到,我们使用这种命令式代码可以做到极致的性能优化,因为我们知道哪里发生变化了,我们直接用命令去更新即可。

如果这个场景换成Vue这种声明式代码呢?会经历如下过程

  1. 首次渲染时,将真实DOM转换为虚拟DOM。
  2. 代表内容的变量字段发生变化,修改新的虚拟DOM树。
  3. 对比新旧虚拟DOM,找到差异。
  4. 根据差异更新给真实DOM树

可以看到,在有虚拟DOM后,需要先去找到差异,再去更新真实DOM。这会比我们直接写真实DOM多一些性能消耗,所以这时候虚拟DOM并没有比真实DOM性能消耗更小。

有人会问了,那为什么还要使用虚拟DOM

原因有两条:

  1. 在实际开发中,我们很难写出绝对优化的命令式代码
  2. 类似原生js或JQuery的命令式代码在开发中,我们需要手动完成真实DOM的创建、编辑、删除等工作,提升了开发过程中的心智负担。

回到问题本身,虚拟DOM真实DOM性能消耗更小么?

完整的答案应该是,在绝对优化代码的情况下,虚拟DOM真实DOM性能消耗更大,因为要多一个找到差异的性能消耗。但在实际开发过程中,虚拟DOM真实DOM性能消耗更小,因为我们写的代码无法做到绝对优化。

# 总结

虚拟DOM是一个JavaScript对象的表示,它是对真实DOM的轻量级抽象。在传统的前端开发中,我们直接操作真实的DOM来进行页面的更新和渲染。每当我们修改DOM时,浏览器都需要重新计算样式、布局和绘制,这个过程被称为"重绘"(repaint)和"回流"(reflow),并且往往是昂贵且耗时的操作。

虚拟DOM通过在内存中构建一个轻量级的DOM树来解决这个问题。当应用程序的状态发生变化时,我们首先在虚拟DOM上进行修改,而不是直接操作真实DOM。通过比较新旧虚拟DOM的差异,我们可以确定哪些部分需要更新,并仅对这些部分进行实际的DOM操作。这种优化技术被称为"DOM Diffing"。

虚拟DOM的工作流程如下:

  1. 初始渲染:首次渲染时,将真实DOM转换为虚拟DOM。
  2. 状态变更:当应用程序的状态发生变化时,修改虚拟DOM树。
  3. 虚拟DOM Diffing:比较新旧虚拟DOM树的差异,找出需要更新的部分。
  4. 生成变更集:根据差异生成最小化的DOM操作序列。
  5. 应用变更:将变更应用于真实DOM,更新页面展示。

通过使用虚拟DOM,我们可以减少真实DOM操作的次数,并最大程度地减少重绘和回流的成本。这种优化能够显著提升性能,特别是在大型复杂的应用程序中,或是需要频繁更新的动态内容中。