什么是 React Diff,对比 Vue Diff ?

347 阅读3分钟

最近在刷 leetcode的前端通关手册,整理了点东西

虚拟 DOM 的 Diff 算法

将新旧虚拟 DOM 看作两棵节点树,节点个数为 n ,通常对比更新需要执行以下步骤:

  • 左侧树的节点需要与右侧树的节点一一对比,需要 O(n²) 复杂度
  • 删除未找到的节点,需要再找合适节点放到被删除位置,需要 O(n) 复杂度
  • 添加新节点,需要 O(n) 复杂度

综上,Diff 虚拟 DOM 的复杂度是 O(n³)

而React 基于以下两个假设的基础之上提出 O(n) 的启发式算法

  • 两个不同类型的元素会产生不同的树
  • 可以通过设置 key 属性,来告知渲染哪些子元素在不同的渲染下可以保存不变

React Diffing 算法

Tree Diff

对比两棵树时,首选比较两棵树的根节点。不同类型的根节点元素会有不同的形态

对于根节点为不同类型的元素,React 会拆卸原有的树并且建立起新的树。当卸载一棵树时,对应的 DOM 节点会被销毁。而建立一棵新的树时,对应的 DOM 节点会被创建以及插入到 DOM 中。

当根节点为相同类型的元素,React 会保留节点,同时对比更新有改变的属性。

处理完根节点,React 继续对子节点进行递归

通过 updateDepth 控制 Virtual Dom 树层级 只比较同一个父节点的子节点(横向比较) 通过删除和创建节点实现跨层级移动 避免跨层级移动,优先 CSS 控制显示隐藏

Component Diff

同类型组件: 组件实例会保持不变,因此可以在不同的渲染时保持 state 一致 React 将更新该组件实例的 props 以及保证与最新的元素保持一致 调用该实例的 UNSAFE_componentWillReceiveProps、 UNSAFE_componentWillUpdate() 以及 componentDidUpdate() 方法 调用 render() 方法,diff 算法将在之前的结果以及新的结果中进行递归 通过 shouldComponentUpdate、useMemo、useCallback 手动优化

不同类型组件: 删除,创建

Vue 2.x 优化 Diff 算法

基本优化与 React 相同,但使用的是双端 diff 算法,去解决一些没必要的移动。即虚拟 DOM 双指针,与真实 DOM 双指针,一一对应(4 个指针,分别指向新旧两个 vnode 数组的头尾)通过两端到中间,直到虚拟 DOM 或真实 DOM,左指针 > 右指针

Vue 3.x 优化的 Diff 算法内容

  • 创建 VNode 确定类型,内容不会变化的 DOM 添加静态标记。同时,在 mount / patch 中用位运算判断 VNode 类型。
  • 静态提升 hoistStatic。不参与更新的元素,只创建一次,渲染时直接复用
  • 事件侦听器缓存 cacheHandlers。缓存函数,不追踪变化,提升性能

后续将持续补充更新,欢迎批评指正

httpweixin.qq.comr5RGXj2jETTUyrSzs90T3.png

来源:前端妙妙屋 - 前端开发者的学习资源