Virtual DOM (虚拟DOM)的实现原理

Virtual DOM 的实现原理

Virtual DOM:虚拟DOM。

学习目标:

  • 了解什么是虚拟DOM,Vue和React内部为什么使用虚拟DOM(虚拟DOM的作用)
  • Vue内部的虚拟DOM是基于Snabbdom库改造的。
    • Snabbdom的基本使用
    • Snabbdom的源码解析
      • 通过源码解析更好的了解虚拟DOM的工作原理

什么是Virtual DOM

Virtual DOM(虚拟DOM),是由普通的 JS 对象来描述DOM对象,因为不是真实的DOM对象,所以叫 Virtual DOM。

真实DOM和虚拟DOM对比

真实DOM:

通过打印一个真实DOM的成员(div的属性)发现,一个DOM对象(div)的成员非常多,所以创建一个DOM对象成本是非常高的。

PS: DOM对象的成员继承自原型链(HTML元素接口:div继承自HTMLDivElement),不能用Object.keys获取到。

MDN - 文档对象模型(DOM) - HTML元素接口

let element = document.createElement('div')
// DOM对象的成员继承自原型链(div继承自HTMLDivElement),不能用Object.keys获取到
// console.log(Object.keys(element))
let s = ''
for (var key in element) {
  s += key + ','
}
console.log(s)

虚拟DOM:

使用Virtual DOM来描述真实DOM:

{
  sel: 'div', // 标签
  data: {},
  children: undefined,
  text: 'Hello Virtual DOM', // 文本内容
  elm: undefined,
  key: undefined
}

可以发现创建一个虚拟DOM,它的成员是非常少的。

也就是创建一个虚拟DOM的成本要小于创建一个真实DOM。

为什么使用 Virtual DOM

  • 手动操作 DOM 比较麻烦,还需要考虑浏览器兼容性问题。
  • 虽然有 jQuery 等库简化 DOM 操作并解决了兼容性问题。
  • 但是随着项目的复杂,DOM操作复杂提升,既要考虑操作数据,又要考虑操作 DOM。
  • 为了简化 DOM 的复杂操作,于是出现了各种 MVVM框架,MVVM 框架解决了视图和状态的同步问题,也就是:
    • 当数据发生变化,自动更新视图
    • 当视图发生变化,自动更新数据
  • 过去,为了简化视图的操作,可以使用模板引擎。
  • 但是模板引擎没有解决跟踪状态变化的问题,即当数据发生变化的时候,无法获取上一次的状态。只好把页面中的元素删除,然后重新创建。无法最小范围更新视图。
  • 于是 Virtual DOM 出现了。
  • Virtual DOM 的好处是,当状态改变时不需要立即更新 DOM,只需要创建一个虚拟 DOM 树来描述 DOM,Virtual DOM 内部将弄清楚如何有效(diff)的更新 DOM。
    • 内部使用 diff 算法,找到状态的差异,只更新变化的部分。

Github 上 virtual-dom 描述

Github 上 virtual-dom 开源库的描述

虚拟DOM是一个 JavaScript DOM模型,支持create Element元素创建、diff差异计算和patch补丁操作(将差异更新到视图),以实现高效的重新渲染。

动机

手动操作比较麻烦,并且很难跟踪以前的DOM状态。

解决此问题的方法是编写代码,就像在状态变化时重新创建整个DOM一样。

当然,如果您每次更改应用程序状态时,都重新创建整个DOM,那您的应用程序将非常缓慢,并且输入字段将失去焦点。

virtual-dom 是modules模块的集合,旨在提供声明性的方式来表示应用程序的DOM。

因此,无需在应用程序状态更改时更新 DOM,只需创建一个用于描述您想要的DOM状态的虚拟树或VTree。

然后 virtual-dom 将在不重新创建所有 DOM 节点的情况下,找出如何更有效的将DOM更新为您描述的状态。

virtual-dom 实现在状态发生变化时更新视图的方式是:

创建完整的VTree的视图,高效的修补dom,使其更新为您描述的状态。

这样可以避免在代码中跟踪之前的状态以及手动操作DOM,从而为web应用提供了清晰且可维护的呈现逻辑。

总结:

  • 虚拟 DOM 可以维护程序的状态,跟踪上一次的状态
  • 通过比较前后两次状态的差异更新真实 DOM

虚拟 DOM 的作用及虚拟 DOM 库

虚拟 DOM 的作用

  • 维护视图和状态的关系
    • 虚拟DOM可以记录上一次数据的变化,只更新状态变化的部分
  • 复杂视图情况下提升渲染性能
    • 比较数据变化差异,只更新差异部分的场景,使用虚拟DOM性能更优
  • 除了渲染成 真实DOM 以外,还可以渲染成其他平台使用的内容:
    • 实现SSR(服务端渲染),把虚拟DOM转换成普通的HTML字符串,常用框架:
      • Nuxt.js - 基于vue的服务端渲染框架
      • Next.js - 基于React的服务端渲染框架
    • 原生应用(Weex/React Native)
    • 小程序(mpvue/uni-app)

Virtual DOM (虚拟DOM)的实现原理_第1张图片

性能对比

任何情况下,直接操作真实DOM,性能肯定高于使用虚拟DOM渲染这个完整的DOM,因为虚拟DOM还要进行转化为真实DOM的操作。

上面说的虚拟DOM性能更优,指的是在某些场景下的某些操作。

虚拟DOM的性能优势在于对DOM的子孙节点进行增删改操作。

操作类型 真实DOM 虚拟DOM
增加节点 获取父节点,添加子节点 减少逻辑代码:不需要再编写获取父节点的代码,直接在新的虚拟DOM中添加子节点,自动同步
删除节点 获取父节点,找到子节点,执行删除 减少逻辑代码:原理同上
修改子节点属性 获取子节点,找到子节点,修改属性 减少逻辑代码:原理同上
在不同层级添加修改节点 方式1:编写大量定位代码,和操作DOM的代码 减少逻辑代码:原理同上
在不同层级添加修改节点 方式2:新建一个DOM替换,其中包含一些重复的内容,消耗资源 最小范围操作DOM:不需要新建,只需对比新旧虚拟DOM,只更新差异的地方
对列表排序 创建新的列表替换 最小范围操作DOM:原理同上
  1. 减少逻辑代码
    1. 虽然在某些时候,虚拟DOM损失了一些性能
    2. 但是它减少了操作DOM的逻辑代码,是实现数据驱动的重要部分。
  2. 最小范围操作DOM:最小范围更新,只更新差异。
    1. 它相比于新建一个DOM,成本小很多:
      1. 复杂DOM更新时,有很多内容没有变化,不需要重新渲染。
      2. 减少重新渲染的未变化部分,也就是减少重绘的可能性,降低浏览器消耗。

Virtual DOM 开源库

  • Snabbdom
    • Vue 2.x 内部使用的 Virtual DOM 就是改造的 Snabbdom
    • 源代码只有大约200 SLOC(Single Line Of Code)(行)
      • 所以学习虚拟DOM,研究Snabbdom的源代码比直接查看Vue的源代码轻松的多
    • 代码量少,但是可以通过模块扩展功能
      • Snabbdom中的模块类似插件的机制
    • 源码基于TypeScript开发
    • 官宣:自己是最快的 Virtual DOM 之一
  • virtual-dom - 最早的虚拟DOM的开源库

你可能感兴趣的:(前端基础)