React19.x 一个示例来看 Diff 算法

setStatescheduleUpdateOnFiberrender 阶段(包含 beginWorkreconcileChildren ← Diff 发生处 → completeWork → commit 阶段 → DOM mutation。

第一轮遍历(从左到右快速匹配,复杂度 O(n)): 同时遍历新旧节点数组,尽可能复用 key 和 type 都相同的前缀节点。如果遇到 key 相同但 type 不同的情况,React 会直接丢弃旧节点并基于新的 React Element 生成全新 Fiber(旧节点放入 deletions 数组待删除),但遍历并不会终止------这说明 React 对待"key 相同、类型变了"的策略是继续往后尝试匹配。如果遇到 key 和 type 都不相同的情况,则立即结束遍历。

第二轮遍历(处理边界):

  • 情况一 :旧节点有剩余,新节点已遍历完 → 将剩余旧节点标记为 Deletion
  • 情况二:新节点有剩余,旧节点已遍历完 → 为剩余新节点创建新的 Fiber;

第三轮遍历(处理剩余节点): 如果第二轮结束后还有新旧节点未被处理,进入第三轮:

两者都有剩余 → 将剩余旧节点按 key 放入 Map,然后遍历剩余新节点,从 Map 中寻找可复用的节点。在这轮遍历中,React 通过维护 lastPlacedIndex(上一个未移动节点在旧数组中的最大索引)来判断节点是否需要移动。

示例

js 复制代码
import { useState } from 'react';

const RenderArray = () => {
  const [array, setArray] = useState<string[]>(['A','B','C','D','E']);

  return (
    <div key="render-array">
      <h1>RenderArray</h1>
      <button onClick={() => setArray(['A','B','E','D','C','F'])}>Set Array</button>
      <ul>
        {array.map((item) => (
          <li key={item}>{item}</li>
        ))}
      </ul>
    </div>
  );
};
export default RenderArray;

reconcileChildrenArray

阶段 1:第一轮遍历(同步遍历新旧节点,快速复用同位置节点)

1、A节点

updateSlot 复用节点

updateElement

placeChild。负责确定新 Fiber 节点在 Fiber 树中的位置,并根据情况标记该节点是需要移动、插入还是保持原位,同时返回更新后的 lastPlacedIndex 值。

A 节点,不需要移动。

2、B节点 与A 节点一样的逻辑

3、新节点 E 、旧节点 C,两者 不同

退出当前第一阶段

阶段 2:处理边界情况(旧节点耗尽 或 新节点遍历完)

情况 2.1:新节点遍历完(newIdx === newChildren.length)

情况 2.2:旧节点遍历完(oldFiber === null)

当前情节跳过,进入第三阶段。

阶段 3:第三轮遍历(键映射表复用 + 计算节点移动)

步骤1:构建剩余旧节点的键映射表(key → Fiber)

步骤2:遍历剩余的新节点,从映射表中查找可复用节点

1、E节点, 找到复用。

不移动

2、找到 D 节点,复用,需要移动

3、找到 C节点,复用,需要移动

4、F节点,新节点,标记插入

创建新节点 createFiberFromElement

标记插入

步骤3:标记删除映射表中剩余的旧节点(无新节点匹配)

相关推荐
fthux8 分钟前
RenoPit 能为普通业主做什么?看懂图纸、审查合同,提前发现装修坑
javascript·人工智能·ai·开源·github·chrome扩展·open source·edge扩展·firefox扩展
仿生狮子1 小时前
✂️ Nuxt 最简单的字体裁剪工具:Fontize
javascript·vue.js·nuxt.js
魔力女仆4 小时前
分享一个 JS 鼠标跟随贪吃蛇背景库
开发语言·javascript·计算机外设
别惊醒渔人6 小时前
Vue3 Diff 优化:最长递增子序列 LIS
前端·javascript·vue.js
Larcher7 小时前
从“加载模型”界面到端侧推理:拆解一个 React + WebGPU 大模型 Demo
javascript·后端
Larcher7 小时前
从状态快照到惰性初始化:读懂 React useState 的三个关键场景
javascript·人工智能·后端
Hilaku9 小时前
工作 5 年后,决定你薪资上限的究竟是什么?
前端·javascript·程序员
weixin_BYSJ19879 小时前
springboot校园自习室管理小程序---附源码32142
java·javascript·spring boot·python·django·flask·php
gis开发之家10 小时前
《Vue3 从入门到大神40篇》Vue3 源码详解(十):diff 算法全解析 —— 为什么 Vue3 比 Vue2 更快?
javascript·算法·typescript·前端框架·vue3·vue3源码
海带紫菜菠萝汤11 小时前
WebCodecs API 实战:浏览器原生视频编解码的原理与性能测试
前端·javascript·音视频·视频编解码