17.1.1 传统Diff算法问题
传统虚拟 DOM 的 Diff 只能按层级「一层一层」遍历,运行时又拿不到编译期信息,于是大量与变更无关的节点也被比较、被 patch。
一、什么是编译优化
编译优化指编译器把模板编译为渲染函数的过程中,尽可能多地提取关键信息,并以此指导生成最优代码。
优化方向基本一致:尽可能区分动态内容与静态内容,针对不同内容采用不同优化策略。
二、传统 Diff 的开销
<div id="foo">
<p class="bar">{{ text }}</p>
</div>
无论简单 Diff、双端 Diff 还是快速 Diff,比对新旧两棵虚拟 DOM 树时,总是按虚拟 DOM 的层级结构「一层一层」遍历:
- 对比
div节点,以及它的属性和子节点; - 对比
p节点,以及它的属性和子节点; - 对比
p的文本子节点:内容变了就更新,没变则什么都不做。
模板结构非常稳定,真正可能变的只有
{{ text }},但传统 Diff 依然走完整条路径,多做了两次无意义的比较。
三、为什么虚拟 DOM 会有额外开销
- 根本原因:渲染器在运行时得不到足够的信息。传统 Diff 无法利用编译期提取到的任何关键信息,所以渲染器不可能在运行时做相关优化。
- 编译期可分析出的关键信息:模板结构稳定、哪些节点是静态的、哪些是动态的。
- Vue.js 3 的解法:编译器把编译期得到的关键信息「附着」在它生成的虚拟 DOM 上,随虚拟 DOM 一起传给渲染器;渲染器据此走「快捷路径」,从而提升运行时性能。
如果编译器能直接生成原生 DOM 操作的代码,甚至可以抛掉虚拟 DOM,避免虚拟 DOM 带来的性能开销。
相关
- 17.1.2 Block 与 PatchFlags:附着的关键信息具体是什么,以及渲染器如何消费它