# 理解虚拟 DOM ,React 选择它是为了性能吗?

# 虚拟 DOM 是什么

  1. 虚拟 DOM 是 JS 对象
  2. 虚拟 DOM 是对真是 DOM 的描述

虚拟 DOM 的本质是 JS 和 DOM 之间的一个映射缓存,它形态上表现为一个能够描述 DOM 结构及其属性信息的 JS 对象。

# React 中的虚拟 DOM 是怎么工作的

  • 挂载阶段: React 将结合 JSX 描述,构建出虚拟 DOM 数,然后通过 ReactDOM.render 实现虚拟 DOM 到真是 DOM 的映射。
  • 更新阶段:页面的变化在作用于真实 DOM 之前,会先作用于虚拟 DOM,虚拟 DOM 将在 JS 层借助算法先对比出具体有哪些真实 DOM 需要被改变,然后将这些改变作用于真实 DOM。

# 前端 DOM 操作解决方案的发展史

  1. 原生 JS 操作的时期
  • 在前端这个工种的萌芽阶段,前端页面“展示”的属性远远强于其“交互”的属性,这就导致 JS 的定位只能是“辅助”:在很长一段时间里,前端工程师们会花费大量的时间去实现静态的 DOM,待一切结束后,再补充少量 JS,实现一些类似于拖拽、隐藏、幻灯片之类的“特效”。
  1. 解放生产力的先导阶段 jQuery 时期
  • 随着开始最求更加丰富的用户体验,前端交互带来大量的 DOM 操作,人们发现 JS 原生提供的 DOM API,太难用了,为了实现高效的开发,jQuery 就诞生了,它将 DOM API 封装为了相对简单和优雅的形式,同时一口气做掉了跨浏览器的兼容工作,并且提供了链式 API 调用、插件扩展等一系列能力用于进一步解放生产力。
  • jQuery 使 DOM 操作变得简单、快速,并且始终确保其形式稳定、可用性稳定。
  1. 模板引擎的方案
  • jQuery 帮助我们能够以更舒服的姿势操作 DOM,但它并不能从根本上解决 DOM 操作量过大情况下前端侧的压力。
  • 什么是模板引擎,大概内容数据如下:
const data = [
  {
    name: 'achang',
    age: 18
  },
  ...
]
1
2
3
4
5
6
7
  • 使用模板
<table>
  {% data.forEach(function(p){ %}
  <tr>
    <td>{% p.name %}</td>
    <td>{% p.age %}</td>
  </tr>
  {% }); %}
</table>

1
2
3
4
5
6
7
8
9
  • 其实就是把 JS 和 HTML 结合在一起的一种规则。把数据源读进去,塞到预置好的 HTML 模板里,然后把两者融合在一起,吐出一段目标字符串。这段字符串的内容,其实就是一份标准的、可用于渲染的 HTML 代码,它将对应一个 DOM 元素。最后,将这个 DOM 元素挂载到页面中去。
  • 模板引擎一会会做几件事:
    1. 读取 HTML 模板并解析它,分离其中的 JS 信息
    2. 将解析出的内容拼接成字符串,动态生成 JS 代码;
    3. 运行动态生成的 JS 代码,返回目标 HTML
    4. 将 HTML 赋值给 innerHTML,触发渲染,生成真实 DOM。

但是它在性能上的表现并不尽如人意:由于不够 “智能”,它更新 DOM 的方式是将已经渲染出 DOM 整体注销后再整体重渲染,并且不存在更新缓冲这一说。在 DOM 操作频繁的场景下,模板引擎可能会直接导致页面卡死。

  1. 虚拟 DOM
  • 利用“数据驱动视图”
  • 虚拟 DOM 的好处是:当 DOM 操作(渲染更新)比较频繁时,它利用 diff 算法先将前后两次的虚拟 DOM 树进行对比,定位出具体需要更新的部分,生成一个“补丁集”,最后只把“补丁”打在需要更新的那部分真实 DOM 上,实现精准的 “差量更新”

# 为什么虚拟 DOM 的价值不在性能

  • 虚拟 DOM 并不一定会带来更好的性能,React 官方也从来没有把虚拟 DOM 作为性能层面的卖点对外输出过。
  • 虚拟 DOM 的优越之处在于,它能够在提供更爽、更高效的研发模式(也就是函数式的 UI 编程方式)的同时,仍然保持一个还不错的性能

# 虚拟 DOM 对比模板引擎

  • 模板引擎是:

    1. 直接生成新的 DOM
    2. 替换旧的 DOM
  • 虚拟 DOM 是:

    1. 先构建新的虚拟 DOM 树
    2. 使用 diff 对比新旧两棵树的差异
    3. 更新差异 DOM
  • 模板1 和 虚拟1,2 都是 JS 范畴内,放到一起看,动态生成 HTML 字符串性能消耗有限,而“虚拟DOM”构建和 diff 算法逻辑规则相对复杂,避免不了涉及到递归,遍历等耗时操作,因为这个JS层面,虚拟DOM并不比模板引擎快。

  • 对比 模板2 和虚拟3 都是属于 DOM 范畴,模板渲染是全量更新,而虚拟 DOM 是差量更新。 乍一看可能差量更新高效,可需要考虑这种情况,数据内容变化非常大(或者说整个发生了改变),促使差量更新计算出来的结果和全量更新极为接近,而在情况下,虚拟 DOM 还有大量的 JS 开销时间,此时会有一个现象就是二者可能难分伯仲,但是如果 虚拟 DOM 在 DOM 操作上比模板引擎快,那么他们的系性能差距可是巨大的,虚拟 DOM 完胜模板引擎。因为虚拟 DOM 的劣势主要在于 JS 计算的耗时,而 DOM 操作的能耗和 JS 计算的能耗根本不在一个量级极少量的 DOM 操作耗费的性能足以支撑大量的 JS 计算

# 那么虚拟 DOM 的价值到底是什么呢?

  • 虚拟 DOM 解决了一下两个问题
  1. 研发体验、研发效率 的问题:
  • DOM 操作模式的每一次革新,背后都是前端对效率和体验的进一步追求。虚拟 DOM 的出现,为数据驱动视图这一思想提供了高度可用的载体,使得前端开发能够基于函数式 UI 的编程方式实现高效的声明式编程(JSX便是其中一个品类)
  1. 跨平台的问题 (同一套虚拟 DOM 可以在不同的平台渲染)
  • 虚拟 DOM 是对真实渲染内容的一层抽象。