虚拟 DOM(Virtual DOM)
背景
当浏览器获取了一个 HTML 文档后,浏览器内核 webkit 的 WebCore 层中的 HTML 引擎会将该文档解析为 DOM 树,解析出的 DOM 树会交给同处在 WebCore 层的 DOM 模块负责管理,这里的 DOM 树就是真实 DOM。
我们在前端的刀耕火种的时代,都是直接使用 JavaScript 直接操作 DOM,但 Dom 树实际是由 DOM 模块负责管理的,浏览器内核中单独有一块内存来存储 DOM 树,但 JavaScript 引擎和这块内存并无关系,也就是它并不能直接的操作真实的 DOM 树。
为了给予 JavaScript 操作 DOM 的能力,浏览器在全局对象 window 上为 JavaScript 封装了一个 document 对象,在这个对象上提供了大部分 DOM 操作 API(接口由 C++ 实现)。
什么是虚拟 DOM
虚拟 DOM (Virtual DOM )这个概念相信大家都不陌生,从 React 到 Vue ,虚拟 DOM 为这两个框架都带来了跨平台的能力(React-Native 和 Weex)。
实际上它只是一层对真实 DOM 的抽象,以 JavaScript 对象 (VNode 节点) 作为基础的树,用对象的属性来描述节点,最终可以通过一系列操作使这棵树映射到真实环境上。
在 Javascript 对象中,虚拟 DOM 表现为一个 Object 对象。并且最少包含标签名 (tag)、属性 (attrs) 和子元素对象 (children) 三个属性,不同框架对这三个属性的名命可能会有差别。
创建虚拟 DOM 就是为了更好将虚拟的节点渲染到页面视图中,所以虚拟 DOM 对象的节点与真实 DOM 的属性一一照应。
为什么要用到虚拟 DOM
首先,肯定是为了效率。但并不一定用了虚拟 DOM 就能提升效率,就比如 svelet 框架,没用虚拟 DOM 反而效率更高。
第一点,由于 vue 或者是 react 能做到的最小程度的重新渲染程度范围是‘组件’,所以虚拟 DOM 的出现是为了降低重新渲染颗粒度。当数据发生变化时,重新调用 Render 函数,如果里面写的是真实 DOM 元素会产生一定的性能问题(操作 DOM 的代价仍旧是昂贵的,频繁操作还是会出现页面卡顿,影响用户的体验),会大大降低效率。而用虚拟 DOM,就会先生成一个虚拟 DOM 树,使用 diff 算法,将之前的 DOM 树与新的虚拟 DOM 树去做对比,只重新渲染发生变化的部分。
第二点就是,解耦运行环境。Virtual DOM 本质就是用一个原生的 JS 对象去描述一个 DOM 节点。是对真实 DOM 的一层抽象。因为不同的运行环境如移动段或者 pc 端,只有浏览器才有真实 DOM,移动端就用不了真实 DOM。虚拟 DOM 的本质就是一个 js 对象,里面有 props,childrens,children 里面又有 props 和 childrens,像是标签一样嵌套。不同的运行环境就可以来解析这个对象实现页面展示(跨平台性)。
举个例子:
你用传统的原生 api 或 jQuery 去操作 DOM 时,浏览器会从构建 DOM 树开始从头到尾执行一遍流程。
当你在一次操作时,需要更新 10 个 DOM 节点,浏览器没这么智能,收到第一个更新 DOM 请求后,并不知道后续还有 9 次更新操作,因此会马上执行流程,最终执行 10 次流程。
而通过 VNode,同样更新 10 个 DOM 节点,虚拟 DOM 不会立即操作 DOM,而是将这 10 次更新的 diff 内容保存到本地的一个 js 对象中,最终将这个 js 对象一次性 attach 到 DOM 树上,避免大量的无谓计算。
很多人认为虚拟 DOM 最大的优势是 diff 算法,减少 JavaScript 操作真实 DOM 的带来的性能消耗。虽然这一个虚拟 DOM 带来的一个优势,但并不是全部。虚拟 DOM 最大的优势在于抽象了原本的渲染过程,实现了跨平台的能力,而不仅仅局限于浏览器的 DOM,可以是安卓和 IOS 的原生组件,可以是近期很火热的小程序,也可以是各种 GUI
虚拟 DOM 的工作方式
虚拟 DOM 的主要概念是在内存中保留 UI 的虚拟表示,并使用协调(reconciliation)过程将其与真实 DOM 同步。该过程包括三个主要步骤:
- 当用户 UI 发生变化时,将整个用户 UI 渲染到虚拟 DOM 中。
- 计算之前虚拟 DOM 和当前虚拟 DOM 表示形式之间的差异。
- 根据变化差异更新真实 DOM。
虚拟 DOM 的作用
- 我们可以在状态发生变化需要更新视图时,使用虚拟 DOM 在内存中完成修改,减少对真实 DOM 的直接操作次数,维护视图和状态的关系;
- 我们还可以通过 diff 算法比较修改后的虚拟 DOM 和修改前的真实 DOM 的差异,只需去更新真实 DOM 中发生变化的位置,较少重绘次数,提升渲染性能;
- 其它:虚拟 DOM 还作用于如 服务端渲染 SSR、原生应用 React Native、小程序 uni-app 等其它方面;