Appearance
Diff
Diff 过程
将模板编译生成的 render 函数执行,创建组件的虚拟 DOM 树(VNode)。
比较新旧虚拟 DOM 树的差异,并在实际 DOM 上进行相应的更新。Diff 过程采用深度优先遍历算法,从根节点开始比较。
比较节点类型:比较新旧节点的标签名(tag),如果不同,则直接替换整个节点及其子树。
比较节点属性:比较新旧节点的属性(props)和事件监听器(listeners),并更新实际 DOM 上的对应属性和事件。
比较子节点:对新旧节点的子节点进行逐个比较。
如果新节点没有子节点,旧节点有子节点,则移除旧节点的子节点。
如果新节点有子节点,旧节点没有子节点,则添加新节点的子节点。
如果新旧节点都有子节点,则进一步细化比较。Diff 子节点:对新旧节点的子节点列表进行逐对比较。
使用双指针法,在新旧节点的子节点列表中分别设定头尾两个指针。
指针从两端向中间移动,依次比较对应位置的节点。
如果新旧节点相同(key 和 tag 都相同),则递归进行 Diff。
如果没有找到相同的节点,则说明该节点是新节点,需要创建并插入到对应位置。
如果找到相同的节点,则将新节点插入到对应位置,并对该节点进行更新。Diff 完成:当比较完所有子节点后,Diff 过程完成,实际 DOM 上的更新也完成。
Vue3和Vue2在diff算法上做了什么改进
- 静态标记
通过在编译阶段对模板进行静态分析,将那些不会改变的静态节点标记出来。在 diff 过程中,这些静态节点可以被跳过,从而减少了不必要的比较和操作,提高了 diff 的效率。
- Fragments 和容器元素
在 Vue 2 中,模板中只能有一个根元素,即模板中的内容必须包裹在一个父元素中。这在某些情况下可能会导致不必要的 DOM 层级。而 Vue 3 引入了 Fragments(片段)的概念,允许模板中有多个根级别的元素,从而减少了 DOM 层级。此外,Vue 3 还优化了容器元素(container elements)的处理,避免不必要的节点操作。
- 缓存组件
引入了组件级别的缓存,通过缓存组件实例,可以避免在每次渲染时创建和销毁组件实例。这在动态组件或条件渲染等场景下,可以减少不必要的组件销毁和创建操作,提高渲染性能。
- 静态提升
在编译阶段进行了静态提升(Static Hoisting)的优化。静态提升将那些静态的子树提升为常量,避免在每次渲染时重新创建这些静态子树。这种优化减少了运行时的开销,并提高了渲染性能。
- Patch flag
引入了 Patch flag 的概念,它是一个用于标记 VNode 的标志位。Patch flag 可以表示 VNode 的类型和属性是否发生了变化,从而在 diff 过程中可以更快速地确定是否需要更新对应的 DOM 节点。