react的diff算法
这个算法用来比较虚拟dom和真实dom,从而最小化真实dom的更新
本质上是对两颗Fiber树的对比
(在Vue中是对旧VDOM树与新VDOM树的对比)
在不剪枝的情况下,时间复杂度接近O(n^3)
基于最长公共子序列 (LCS) 的朴素计算为O(n2),加上需要递归比较子节点,时间复杂度来到O(n3)
diff的限制:
- 只进行同层级比较
- 新旧节点的type不同直接删除旧节点,创建新节点
- 通过 key 来复用节点
三个层级的比较策略
- 第一步剪枝,Tree层级的比较
每次递归开始的第一步,比较两棵树的根节点,不一样直接销毁整棵树重建 - Component层级
type同 => 保持组件实例不变只更新props,然后进入组件的render结果进行Element层级的比较
type不同 => 销毁重建 - 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. 第二轮
-
newChildren 与 oldFiber 同时遍历完
只需在第一轮遍历进行组件更新。此时 Diff 结束。 -
newChildren 没遍历完,oldFiber 遍历完
遍历剩下的 newChildren 为生成的 fiber 依次标记 Placement。 -
newChildren 遍历完,oldFiber 没遍历完
遍历剩下的 oldFiber,依次标记 Deletion。 -
newChildren 与 oldFiber 都没遍历完
处理移动的节点
react会把剩余的旧 Fiber 及其在旧列表中的索引存入一个 Map 中
每个新节点有两个index,oldIndex 和 maxIndex,
oldIndex:新节点在 Map 中查找对应的旧 Fiber,得到它在旧列表中的原始索引。
maxIndex: 记录当前遇到的所有旧节点中的最大原始索引。
当 oldIndex>maxIndex 时,将 oldIndex 的值赋值给 maxIndex
当 oldIndex=maxIndex 时,不操作
当 oldIndex<maxIndex 时,将当前节点移动到 index 的位置

浙公网安备 33010602011771号