🍪🧁🍧

react的diff算法

这个算法用来比较虚拟dom和真实dom,从而最小化真实dom的更新

本质上是对两颗Fiber树的对比
(在Vue中是对旧VDOM树与新VDOM树的对比)

在不剪枝的情况下,时间复杂度接近O(n^3)

基于最长公共子序列 (LCS) 的朴素计算为O(n2),加上需要递归比较子节点,时间复杂度来到O(n3)

diff的限制:

  1. 只进行同层级比较
  2. 新旧节点的type不同直接删除旧节点,创建新节点
  3. 通过 key 来复用节点

三个层级的比较策略

  1. 第一步剪枝,Tree层级的比较
    每次递归开始的第一步,比较两棵树的根节点,不一样直接销毁整棵树重建
  2. Component层级
    type同 => 保持组件实例不变只更新props,然后进入组件的render结果进行Element层级的比较
    type不同 => 销毁重建
  3. Element层级
    type同 => 更新属性
    type不同 => 销毁重建

在 React 中,一个 Fiber 节点(即组件实例)的状态(State)和 Hooks(如 useState, useEffect)是严格绑定在它的 type 上的。让一个不再渲染的旧组件 Fiber 保持 Hooks 激活状态是错误的,会导致内存泄漏和不可预测的行为,必须立即销毁重建。

diff算法

根据新fiber树的节点的child类型判断是单节点diff还是多节点diff,仅当child类型为数组时进行多节点diff

1. 单节点diff

key同 且 type不同 => 执行 deleteRemainingChildren 将 child 及其兄弟 fiber 都标记删除。
key不同 => 将child标记删除,然后遍历兄弟fiber

因为新的fiber节点只有一个,旧fiber的第一个child type不同的话其他child也不可能相同

2.多节点diff

两轮遍历

1. 第一轮

key同 type同可复用
key 不同导致不可复用,立即跳出整个遍历,第一轮遍历结束。

第一个元素key不同,之后的key一般也不同
key 相同 而type 不同导致不可复用,会将 oldFiber 标记为 DELETION,并继续遍历
newChildren和oldFiber其一遍历完成就跳出

2. 第二轮

  1. newChildren 与 oldFiber 同时遍历完
    只需在第一轮遍历进行组件更新。此时 Diff 结束。

  2. newChildren 没遍历完,oldFiber 遍历完
    遍历剩下的 newChildren 为生成的 fiber 依次标记 Placement。

  3. newChildren 遍历完,oldFiber 没遍历完
    遍历剩下的 oldFiber,依次标记 Deletion。

  4. newChildren 与 oldFiber 都没遍历完
    处理移动的节点

react会把剩余的旧 Fiber 及其在旧列表中的索引存入一个 Map 中
每个新节点有两个index,oldIndex 和 maxIndex,
oldIndex:新节点在 Map 中查找对应的旧 Fiber,得到它在旧列表中的原始索引。
maxIndex: 记录当前遇到的所有旧节点中的最大原始索引。

当 oldIndex>maxIndex 时,将 oldIndex 的值赋值给 maxIndex
当 oldIndex=maxIndex 时,不操作
当 oldIndex<maxIndex 时,将当前节点移动到 index 的位置

posted @ 2025-10-24 20:44  不想吃fun  阅读(8)  评论(0)    收藏  举报