虚拟DOM原理,性能分析
命令式和声明式
- 命令式
-
- 关注过程
- 描述的是“做事的过程”,这符合我们的逻辑直觉
- 声明式
-
- 关注结果
- 内部由命令式实现
性能与可维护性的权衡
声明式代码的性能不优于命令式代码的性能。
- 性能对比
-
- 把直接修改的性能消耗定义为 A,把找出差异的性能消耗定义为 B
- 命令式代码的更新性能消耗 = A
- 声明式代码的更新性能消耗 = B + A
- 框架本身就是封装了命令式代码才实现了面向用户的声明式
- 声明式代码的可维护性更强,在保持可维护性的同时让性能损失最小化
虚拟DOM的性能到底如何
虚拟 DOM,就是为了最小化找出差异的性能消耗
- 声明式代码的更新性能消耗 = 找出差异的性能消耗+ 直接修改的性能消耗
innerHTML和虚拟DOM操作元素性能比较
-
- innerHTML 操作
-
-
- 字符串解析成 DOM 树,DOM 层面的计算(DOM 的运算要远比 JavaScript 层面的计算性能差)
- HTML 字符串拼接的计算量 + innerHTML 的 DOM计算量
-
-
- 虚拟DOM操作
-
-
- 创建 JavaScript 对象,这个对象可以理解为真实 DOM 的描述
- 递归地遍历虚拟 DOM 树并创建真实 DOM
- 创建 JavaScript 对象的计算量 + 创建真实 DOM 的计算量
-
innerHTML和虚拟DOM更新页面时的性能比较
-
- 使用 innerHTML
-
-
- 重新构建 HTML 字符串
- 重新设置 DOM 元素的 innerHTML 属性(只更改了一个文字,也要重新设置 innerHTML 属性)
- 重新设置innerHTML 属性就等价于销毁所有旧的 DOM 元素,再全量创建新的 DOM 元素
-
-
- 使用虚拟DOM
-
-
- 重新创建JS对象(虚拟DOM树)
- 对比新旧虚拟DOM
- 找到变化的元素并更新
-
innerHTML操作与虚拟DOM性能消耗完整对比
-
- 虚拟 DOM 在 JavaScript 层面的运算要比创建页面时多出一个 Diff 的性能消耗
- Diff计算是是 JavaScript 层面的运算,所以不会产生数量级的差异
- DOM 层面的运算
-
-
- 虚拟 DOM 在更新页面时只会更新必要的元素
- innerHTML需要全量更新
-
-
- 虚拟无论页面多大,都只会更新变化的内容
- innerHTML页面越大,就意味着更新时的性能消耗越大
综合对比
运行时和编译时
有没有办法做到,既声明式地描述UI,又具备原生 JavaScript 的性能
纯运行时
-
- 提供一个 Render 函数
- 为该函数提供一个树型结构的数据对象
- Render 函数会根据该对象递归地将数据渲染成 DOM 元素
- 用户使用过程
-
-
- 实现 Render 函数
- 实现 Render 函数
- 使用Render函数
-
-
- 不能支持用类似于 HTML 标签的方式描述树型结构的数据对象
运行时+编译时
-
- 将HTML 标签编译成树型结构的数据对象
-
- 分别调用 Compiler 函数和 Render 函数
-
- 特点
-
-
- 支持运行时,用户可以直接提供数据对象从而无须编译
- 支持编译时,用户可以提供 HTML 字符串,我们将其编译为数据对象后再交给运行时处理
- 运行时编译,代码运行的时候才开始编译,会产生一定的性能开销
- 可以在构建的时候就执行 Compiler 程序将用户提供的内容编译好,极大提高性能
-
纯编译时
- 将 HTML 字符串编译为命令式代码
总结
- 命令式在理论上可以做到极致优化,但是用户要承受巨大的心智负担
- 而声明式能够有效减轻用户的心智负担,但是性能上有一定的牺牲
- 声明式的更新性能消耗 = 找出差异的性能消耗 + 直接修改的性能消耗
- 虚拟 DOM 的意义就在于使找出差异的性能消耗最小化
- Vue.js 3 是一个编译时 + 运行时的框架,它在保持灵活性的基础上,还能够通过编译手段分析用户提供的内容,从而进一步提升更新性能