第四部分:Fiber架构与并发渲染:React内核源码级剖析
第7章 React Fiber架构:可中断的渲染调度机制
7.1 Fiber节点的数据结构全景
Fiber架构是React 16引入的核心重构,它彻底改变了React的协调(Reconciliation)机制,使得渲染可以被中断和恢复,为并发特性奠定了基础。
7.1.1 链表表示的三叉树
Fiber使用链表结构来表示组件树,这种设计使得遍历可以灵活地中断和恢复。
child、sibling、return指针的遍历算法与栈模拟
TEXT1Fiber节点结构: 2 3interface Fiber { 4 // 实例相关 5 stateNode: any; // 对应的DOM节点或组件实例 6 7 // 工作相关 8 pendingProps: Props; // 新的Props 9 memoizedProps: Props; // 已应用的Props 10 memoizedState: any; // 已处理的状态(Hooks链表) 11 updateQueue: UpdateQueue; // 更新队列 12 13 // 关系指针 14 child: Fiber | null; // 第一个子节点 15 sibling: Fiber | null; // 下一个兄弟节点 16 return: Fiber | null; // 父节点 17 18 // 副作用相关 19 flags: Flags; // 副作用标记 20 subtreeFlags: Flags; // 子树副作用标记 21 22 // 双缓冲 23 alternate: Fiber | null; // 对应的工作单元(current/workInProgress) 24} 25 26树形结构示例: 27 28 A (return: null) 29 / \ 30 B C (sibling: null) 31 / \ \ 32 D E F 33 34链表表示: 35A.child = B 36B.sibling = C 37B.child = D 38D.sibling = E 39C.child = F 40B.return = A 41C.return = A 42D.return = B 43E.return = B 44F.return = C
深度优先遍历的递归改循环优化与空间复杂度分析
TYPESCRIPT1// 递归遍历(传统方式,可能栈溢出) 2function traverseRecursive(fiber: Fiber | null, callback: (f: Fiber) => void) { 3 if (!fiber) return; 4 5 callback(fiber); 6 traverseRecursive(fiber.child, callback); 7 traverseRecursive(fiber.sibling, callback); 8} 9 10// 循环遍历(Fiber使用的方式,可中断) 11function traverseIterative(root: Fiber, callback: (f: Fiber) => void) { 12 let current: Fiber | null = root; 13 14 while (current !== null) { 15 callback(current); 16 17 // 优先访问子节点 18 if (current.child !== null) { 19 current = current.child; 20 continue; 21 } 22 23 // 没有子节点,访问兄弟节点 24 if (current.sibling !== null) { 25 current = current.sibling; 26 continue; 27 } 28 29 // 向上回溯 30 while (current !== null) { 31 if (current.sibling !== null) { 32 current = current.sibling; 33 break; 34 } 35 current = current.return; 36 } 37 } 38} 39 40// 可中断的遍历(React实际使用) 41function* traverseInterruptible(root: Fiber): Generator<Fiber, void, boolean> { 42 let current: Fiber | null = root; 43 44 while (current !== null) { 45 // yield当前节点,外部可以决定是否继续 46 const shouldContinue = yield current; 47 if (!shouldContinue) return; 48 49 if (current.child !== null) { 50 current = current.child; 51 } else if (current.sibling !== null) { 52 current = current.sibling; 53 } else { 54 while (current !== null) { 55 if (current.sibling !== null) { 56 current = current.sibling; 57 break; 58 } 59 current = current.return; 60 } 61 } 62 } 63}
PlantUML图示:Fiber链表结构
渲染图表...
7.1.2 Fiber节点的属性分类
Fiber节点的属性可以分为几个主要类别,每个类别承担不同的职责。
实例相关、工作相关、副作用相关的内存布局
TYPESCRIPT1// Fiber节点完整类型定义(简化版) 2interface Fiber { 3 // ========== 标识 ========== 4 tag: WorkTag; // 组件类型标记 5 key: string | null; // key属性 6 elementType: any; // 元素类型(用于DevTools) 7 type: any; // 组件类型(函数、类、DOM标签) 8 9 // ========== 实例相关 ========== 10 stateNode: any; // 11 // - 对于HostComponent:对应的DOM节点 12 // - 对于ClassComponent:组件实例 13 // - 对于FunctionComponent:null 14 15 // ========== 工作相关 ========== 16 pendingProps: Props; // 新的Props(等待处理) 17 memoizedProps: Props; // 已应用的Props 18 memoizedState: any; // 19 // - 对于ClassComponent:state 20 // - 对于FunctionComponent:Hooks链表头 21 updateQueue: UpdateQueue | null; // 更新队列 22 23 // ========== 关系指针 ========== 24 return: Fiber | null; // 父节点 25 child: Fiber | null; // 第一个子节点 26 sibling: Fiber | null; // 下一个兄弟节点 27 index: number; // 在兄弟节点中的索引 28 29 // ========== 副作用相关 ========== 30 flags: Flags; // 当前节点的副作用 31 subtreeFlags: Flags; // 子树的副作用汇总 32 deletions: Fiber[] | null; // 待删除的子节点 33 34 // ========== 双缓冲 ========== 35 alternate: Fiber | null; // 对应的工作单元 36 // current.alternate = workInProgress 37 // workInProgress.alternate = current 38 39 // ========== 调度相关 ========== 40 lanes: Lanes; // 当前节点的更新优先级 41 childLanes: Lanes; // 子树的更新优先级 42 43 // ========== 调试相关 ========== 44 _debugSource?: Source; 45 _debugOwner?: Fiber; 46 _debugNeedsRemount?: boolean; 47} 48 49// WorkTag枚举 50enum WorkTag { 51 FunctionComponent = 0, 52 ClassComponent = 1, 53 IndeterminateComponent = 2, // 初始渲染前不确定类型 54 HostRoot = 3, // 根节点 55 HostPortal = 4, 56 HostComponent = 5, // DOM节点 57 HostText = 6, // 文本节点 58 Fragment = 7, 59 Mode = 8, 60 ContextConsumer = 9, 61 ContextProvider = 10, 62 ForwardRef = 11, 63 Profiler = 12, 64 SuspenseComponent = 13, 65 MemoComponent = 14, 66 SimpleMemoComponent = 15, 67 LazyComponent = 16, 68 IncompleteClassComponent = 17, 69 DehydratedFragment = 18, 70 SuspenseListComponent = 19, 71 ScopeComponent = 21, 72 OffscreenComponent = 22, 73 LegacyHiddenComponent = 23, 74 CacheComponent = 24, 75 TracingMarkerComponent = 25, 76} 77 78// Flags(副作用标记) 79enum Flags { 80 NoFlags = 0, 81 PerformedWork = 1, 82 Placement = 2, // 需要插入 83 Update = 4, // 需要更新 84 ChildDeletion = 16, // 需要删除子节点 85 ContentReset = 32, 86 Callback = 64, 87 DidCapture = 128, // 捕获到错误 88 Ref = 256, // Ref需要更新 89 Snapshot = 1024, 90 Passive = 2048, // Passive effect(useEffect) 91 Visibility = 4096, 92 StoreConsistency = 8192, 93 // ... 更多标记 94}
7.1.3 双缓冲(Double Buffering)技术
双缓冲是React实现高效更新的核心技术,它通过维护两棵Fiber树来实现。
current树与workInProgress树的alternate指针同步与UI一致性保障
TEXT1双缓冲机制: 2 3阶段1:初始渲染 4- current = null(还没有UI) 5- 创建workInProgress树 6- 渲染完成后,workInProgress成为current 7 8阶段2:更新 9- current = 当前显示的UI对应的Fiber树 10- 基于current创建workInProgress树(alternate关系) 11- 在workInProgress上执行更新 12- 提交完成后,workInProgress成为新的current 13 14alternate关系: 15current Fiber <-> workInProgress Fiber 16 A A' 17 / \ / \ 18 B C B' C' 19 20A.alternate = A' 21A'.alternate = A 22B.alternate = B' 23...
TYPESCRIPT1// 创建workInProgress节点 2function createWorkInProgress(current: Fiber, pendingProps: Props): Fiber { 3 let workInProgress = current.alternate; 4 5 if (workInProgress === null) { 6 // 创建新的workInProgress节点 7 workInProgress = createFiber( 8 current.tag, 9 pendingProps, 10 current.key, 11 current.mode 12 ); 13 14 workInProgress.elementType = current.elementType; 15 workInProgress.type = current.type; 16 workInProgress.stateNode = current.stateNode; 17 18 // 建立alternate关系 19 workInProgress.alternate = current; 20 current.alternate = workInProgress; 21 } else { 22 // 复用现有的workInProgress节点 23 workInProgress.pendingProps = pendingProps; 24 workInProgress.lanes = NoLanes; 25 workInProgress.child = null; 26 workInProgress.memoizedState = null; 27 workInProgress.updateQueue = null; 28 workInProgress.sibling = null; 29 workInProgress.flags = NoFlags; 30 workInProgress.subtreeFlags = NoFlags; 31 workInProgress.deletions = null; 32 } 33 34 workInProgress.lanes = current.lanes; 35 workInProgress.childLanes = current.childLanes; 36 workInProgress.index = current.index; 37 workInProgress.ref = current.ref; 38 39 return workInProgress; 40} 41 42// 提交阶段的原子性 43function commitRoot(root: FiberRoot) { 44 const finishedWork = root.finishedWork; 45 46 if (finishedWork === null) { 47 return; 48 } 49 50 // 原子性地切换current树 51 root.current = finishedWork.alternate; 52 53 // 执行DOM操作 54 commitMutationEffects(root, finishedWork); 55 56 // 切换完成后,UI与current树一致 57}
PlantUML图示:双缓冲机制
渲染图表...
7.2 工作循环(Work Loop)与优先级调度
工作循环是React调度的核心,它负责协调渲染工作,确保浏览器保持响应。
7.2.1 performConcurrentWorkOnRoot的入口调度
performConcurrentWorkOnRoot是并发渲染的入口函数,它负责启动工作循环。
优先级比较与任务饥饿(Starvation)预防机制
TYPESCRIPT1// Lane模型:使用位掩码表示优先级 2export type Lane = number; 3export type Lanes = number; 4 5// 优先级定义(从高位到低位,优先级递减) 6export const SyncLane: Lane = 0b0000000000000000000000000000001; // 同步,最高优先级 7export const InputContinuousHydrationLane: Lane = 0b0000000000000000000000000000010; 8export const InputContinuousLane: Lane = 0b0000000000000000000000000000100; 9export const DefaultHydrationLane: Lane = 0b0000000000000000000000000001000; 10export const DefaultLane: Lane = 0b0000000000000000000000000010000; 11export const TransitionHydrationLane: Lane = 0b0000000000000000000000000100000; 12export const TransitionLane1: Lane = 0b0000000000000000000000001000000; 13export const TransitionLane2: Lane = 0b0000000000000000000000010000000; 14// ... 更多Transition lanes 15export const IdleLane: Lane = 0b0100000000000000000000000000000; // 空闲,最低优先级 16 17// 优先级操作 18function getHighestPriorityLane(lanes: Lanes): Lane { 19 return lanes & -lanes; // 获取最低位的1(最高优先级) 20} 21 22function includesNonIdleWork(lanes: Lanes): boolean { 23 return (lanes & NonIdleLanes) !== NoLanes; 24} 25 26function markStarvedLanesAsExpired(root: FiberRoot, currentTime: number): void { 27 const pendingLanes = root.pendingLanes; 28 const expiredLanes = root.expiredLanes; 29 30 // 检查每个lane的过期时间 31 let lanes = pendingLanes; 32 while (lanes > 0) { 33 const index = pickArbitraryLaneIndex(lanes); 34 const lane = 1 << index; 35 36 const expirationTime = root.expirationTimes[index]; 37 if (expirationTime !== NoTimestamp && expirationTime <= currentTime) { 38 // 标记为过期,提升优先级 39 root.expiredLanes |= lane; 40 } 41 42 lanes &= ~lane; 43 } 44}
Lane模型的位掩码操作与优先级合并
TYPESCRIPT1// Lane操作示例 2const lane1: Lane = 0b001; // Sync 3const lane2: Lane = 0b010; // InputContinuous 4const lane3: Lane = 0b100; // Default 5 6// 合并优先级 7const combined: Lanes = lane1 | lane2 | lane3; // 0b111 8 9// 检查是否包含某个优先级 10const hasSync = (combined & SyncLane) !== 0; // true 11 12// 移除优先级 13const withoutSync = combined & ~SyncLane; // 0b110 14 15// 获取最高优先级 16const highest = combined & -combined; // 0b001 (SyncLane) 17 18// 优先级排序 19function lanesToPriority(lanes: Lanes): number { 20 // 返回优先级数值,越小优先级越高 21 if ((lanes & SyncLane) !== NoLanes) return 1; 22 if ((lanes & InputContinuousLane) !== NoLanes) return 2; 23 if ((lanes & DefaultLane) !== NoLanes) return 3; 24 // ... 25 return 100; // Idle 26}
7.2.2 时间切片(Time Slicing)的实现
时间切片是React实现可中断渲染的关键技术,它确保浏览器主线程保持响应。
MessageChannel的宏任务调度与5ms帧时间预算的shouldYield检查点
TYPESCRIPT1// 调度器使用MessageChannel进行异步调度 2const channel = new MessageChannel(); 3const port = channel.port2; 4 5// 请求调度 6function requestHostCallback(callback: () => void) { 7 scheduledHostCallback = callback; 8 port.postMessage(null); // 触发宏任务 9} 10 11// 处理回调 12channel.port1.onmessage = function() { 13 if (scheduledHostCallback !== null) { 14 const currentTime = getCurrentTime(); 15 16 // 5ms时间片 17 const hasTimeRemaining = currentTime < startTime + 5; 18 19 if (hasTimeRemaining && !shouldYieldToHost()) { 20 // 继续执行工作 21 scheduledHostCallback(); 22 } else { 23 // 时间用完,让出主线程 24 port.postMessage(null); 25 } 26 } 27}; 28 29// shouldYield检查 30function shouldYieldToHost(): boolean { 31 return ( 32 // 1. 帧时间用完(5ms) 33 getCurrentTime() >= deadline || 34 // 2. 有更高优先级的工作 35 needsPaint || 36 // 3. 有紧急任务(如用户输入) 37 hasPendingInput 38 ); 39} 40 41// 工作循环中的yield检查 42function workLoopConcurrent() { 43 while (workInProgress !== null && !shouldYield()) { 44 workInProgress = performUnitOfWork(workInProgress); 45 } 46} 47 48// 帧时间预算的可视化 49/* 50时间轴: 510ms 5ms 10ms 15ms 20ms 52|------|------|------|------| 53[React ][Browser][React ][Browser] 54 工作 事件 工作 渲染 55 56每个5ms时间片: 57- React执行渲染工作 58- 让出主线程处理浏览器事件 59- 下一帧继续 60*/
浏览器主线程协作与卡顿预防
TYPESCRIPT1// 检测长任务 2const observer = new PerformanceObserver((list) => { 3 for (const entry of list.getEntries()) { 4 if (entry.duration > 50) { 5 console.warn('Long task detected:', entry.duration, 'ms'); 6 } 7 } 8}); 9observer.observe({ entryTypes: ['longtask'] }); 10 11// React的时间切片防止长任务 12function performWorkUntilDeadline() { 13 const currentTime = getCurrentTime(); 14 deadline = currentTime + yieldInterval; // yieldInterval = 5ms 15 16 let hasMoreWork = true; 17 try { 18 hasMoreWork = flushWork(currentTime < deadline); 19 } finally { 20 if (hasMoreWork) { 21 // 还有工作,继续调度 22 schedulePerformWorkUntilDeadline(); 23 } else { 24 isMessageLoopRunning = false; 25 } 26 } 27}
7.2.3 批量更新(Batching)的Transaction机制
批量更新是React优化性能的重要手段,它将多个状态更新合并为一次重新渲染。
legacy模式与automatic模式的差异
TYPESCRIPT1// Legacy模式(React 17及之前) 2// 需要显式使用unstable_batchedUpdates 3import { unstable_batchedUpdates } from 'react-dom'; 4 5function LegacyComponent() { 6 const [count, setCount] = useState(0); 7 const [name, setName] = useState(''); 8 9 const handleClick = () => { 10 // 没有batching,会触发两次渲染 11 setCount(c => c + 1); 12 setName('John'); 13 }; 14 15 const handleClickBatched = () => { 16 // 显式batching,只触发一次渲染 17 unstable_batchedUpdates(() => { 18 setCount(c => c + 1); 19 setName('John'); 20 }); 21 }; 22 23 return <button onClick={handleClick}>Click</button>; 24} 25 26// Automatic模式(React 18+) 27// 自动batching,无需显式处理 28function AutomaticComponent() { 29 const [count, setCount] = useState(0); 30 const [name, setName] = useState(''); 31 32 const handleClick = () => { 33 // 自动batching,只触发一次渲染 34 setCount(c => c + 1); 35 setName('John'); 36 }; 37 38 // 异步回调也自动batching 39 const handleAsync = async () => { 40 await fetchData(); 41 // 以下更新会自动batch 42 setCount(c => c + 1); 43 setName('John'); 44 }; 45 46 return <button onClick={handleClick}>Click</button>; 47} 48 49// flushSync:强制同步更新 50import { flushSync } from 'react-dom'; 51 52function FlushSyncExample() { 53 const [count, setCount] = useState(0); 54 55 const handleClick = () => { 56 // 强制同步更新 57 flushSync(() => { 58 setCount(c => c + 1); 59 }); 60 // 到这里DOM已经更新 61 console.log(document.getElementById('count').textContent); // 最新值 62 }; 63 64 return <div id="count">{count}</div>; 65}
7.3 协调算法(Reconciliation)的Diff策略
协调算法是React的核心,它决定了如何高效地更新DOM。
7.3.1 Diff算法的O(n)复杂度保证
React的Diff算法基于两个关键假设,保证了O(n)的时间复杂度。
两假设的数学证明与最坏情况分析
TEXT1React Diff算法的两个假设: 2 3假设1:不同类型的元素会产生不同的树 4假设2:开发者可以通过key属性提示哪些子元素是稳定的 5 6基于这两个假设,Diff算法的时间复杂度为O(n)。 7 8证明: 9- 假设1确保不需要跨类型比较 10- 假设2确保子元素比较是O(n)而非O(n²) 11 12最坏情况: 13- 没有key,所有子元素都改变位置 14- 复杂度退化为O(n²)(但React会给出警告)
TYPESCRIPT1// Diff算法的核心逻辑 2function reconcileChildren( 3 current: Fiber | null, 4 workInProgress: Fiber, 5 nextChildren: ReactElement[], 6 renderLanes: Lanes 7): void { 8 if (current === null) { 9 // Mount阶段:直接创建新Fiber 10 workInProgress.child = mountChildFibers( 11 workInProgress, 12 null, 13 nextChildren, 14 renderLanes 15 ); 16 } else { 17 // Update阶段:执行Diff 18 workInProgress.child = reconcileChildFibers( 19 workInProgress, 20 current.child, 21 nextChildren, 22 renderLanes 23 ); 24 } 25} 26 27// Diff策略 28function reconcileChildFibers( 29 returnFiber: Fiber, 30 currentFirstChild: Fiber | null, 31 newChild: any, 32 lanes: Lanes 33): Fiber | null { 34 // 策略1:单元素Diff 35 if (typeof newChild === 'object' && newChild !== null) { 36 switch (newChild.$$typeof) { 37 case REACT_ELEMENT_TYPE: 38 // 根据key和type匹配 39 return reconcileSingleElement( 40 returnFiber, 41 currentFirstChild, 42 newChild, 43 lanes 44 ); 45 case REACT_PORTAL_TYPE: 46 // Portal处理 47 return reconcileSinglePortal(...); 48 } 49 } 50 51 // 策略2:文本节点Diff 52 if (typeof newChild === 'string' || typeof newChild === 'number') { 53 return reconcileSingleTextNode(...); 54 } 55 56 // 策略3:多子节点Diff 57 if (Array.isArray(newChild)) { 58 return reconcileChildrenArray(...); 59 } 60 61 // 删除剩余子节点 62 return deleteRemainingChildren(returnFiber, currentFirstChild); 63}
7.3.2 单列表Diff的Key匹配算法
单列表Diff是React中最常见的Diff场景,key属性在其中起着关键作用。
lastPlacedIndex的贪心算法与最小移动次数计算
TYPESCRIPT1// 单列表Diff算法 2function reconcileChildrenArray( 3 returnFiber: Fiber, 4 currentFirstChild: Fiber | null, 5 newChildren: ReactElement[], 6 lanes: Lanes 7): Fiber | null { 8 let resultingFirstChild: Fiber | null = null; 9 let previousNewFiber: Fiber | null = null; 10 let oldFiber: Fiber | null = currentFirstChild; 11 let lastPlacedIndex = 0; 12 let newIdx = 0; 13 let nextOldFiber: Fiber | null = null; 14 15 // 第一轮:遍历相同位置的子节点 16 for (; oldFiber !== null && newIdx < newChildren.length; newIdx++) { 17 if (oldFiber.index > newIdx) { 18 nextOldFiber = oldFiber; 19 oldFiber = null; 20 } else { 21 nextOldFiber = oldFiber.sibling; 22 } 23 24 const newFiber = updateSlot( 25 returnFiber, 26 oldFiber, 27 newChildren[newIdx], 28 lanes 29 ); 30 31 if (newFiber === null) { 32 break; 33 } 34 35 if (oldFiber && newFiber.alternate === null) { 36 // 删除了旧节点 37 deleteChild(returnFiber, oldFiber); 38 } 39 40 // 计算放置位置 41 lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx); 42 43 if (previousNewFiber === null) { 44 resultingFirstChild = newFiber; 45 } else { 46 previousNewFiber.sibling = newFiber; 47 } 48 49 previousNewFiber = newFiber; 50 oldFiber = nextOldFiber; 51 } 52 53 // 第二轮:处理剩余的新节点或旧节点 54 // ... 55 56 // 第三轮:移动节点 57 // ... 58 59 return resultingFirstChild; 60} 61 62// placeChild:决定是否移动节点 63function placeChild( 64 newFiber: Fiber, 65 lastPlacedIndex: number, 66 newIndex: number 67): number { 68 newFiber.index = newIndex; 69 70 const current = newFiber.alternate; 71 if (current !== null) { 72 const oldIndex = current.index; 73 if (oldIndex < lastPlacedIndex) { 74 // 需要移动 75 newFiber.flags |= Placement; 76 return lastPlacedIndex; 77 } else { 78 // 不需要移动 79 return oldIndex; 80 } 81 } else { 82 // 新节点,需要插入 83 newFiber.flags |= Placement; 84 return lastPlacedIndex; 85 } 86}
索引作为Key的O(n^2)退化场景与动态列表性能陷阱
TYPESCRIPT1// 问题:使用索引作为key 2function BadList({ items }: { items: Item[] }) { 3 return ( 4 <ul> 5 {items.map((item, index) => ( 6 // 使用index作为key是反模式! 7 <li key={index}>{item.name}</li> 8 ))} 9 </ul> 10 ); 11} 12 13// 问题场景: 14// 原列表:[A, B, C],key分别为[0, 1, 2] 15// 新列表:[X, A, B, C],key分别为[0, 1, 2, 3] 16// React会认为: 17// - 0位置的A变成了X(更新) 18// - 1位置的B变成了A(更新) 19// - 2位置的C变成了B(更新) 20// - 新增C在3位置 21// 实际上所有节点都被更新了! 22 23// 正确做法:使用稳定的唯一标识 24function GoodList({ items }: { items: Item[] }) { 25 return ( 26 <ul> 27 {items.map((item) => ( 28 // 使用item.id作为key 29 <li key={item.id}>{item.name}</li> 30 ))} 31 </ul> 32 ); 33} 34 35// 正确场景: 36// 原列表:[A, B, C],key分别为[id-a, id-b, id-c] 37// 新列表:[X, A, B, C],key分别为[id-x, id-a, id-b, id-c] 38// React会认为: 39// - A、B、C位置不变 40// - 在开头插入X 41// 只有X需要创建,其他节点复用!
7.3.3 多节点Diff的哈希映射
对于复杂的列表更新,React使用哈希映射来优化匹配效率。
Key到Fiber节点的缓存与复用判定
TYPESCRIPT1// 使用Map缓存key到Fiber的映射 2function mapRemainingChildren( 3 currentFirstChild: Fiber 4): Map<string | number, Fiber> { 5 const existingChildren: Map<string | number, Fiber> = new Map(); 6 7 let existingChild: Fiber | null = currentFirstChild; 8 while (existingChild !== null) { 9 if (existingChild.key !== null) { 10 existingChildren.set(existingChild.key, existingChild); 11 } else { 12 existingChildren.set(existingChild.index, existingChild); 13 } 14 existingChild = existingChild.sibling; 15 } 16 17 return existingChildren; 18} 19 20// 在Diff中使用Map查找 21function reconcileChildrenArrayWithMap( 22 returnFiber: Fiber, 23 currentFirstChild: Fiber | null, 24 newChildren: ReactElement[], 25 lanes: Lanes 26): Fiber | null { 27 // ...第一轮遍历... 28 29 // 创建key到Fiber的映射 30 const existingChildren = mapRemainingChildren(oldFiber); 31 32 // 遍历剩余的新子节点 33 for (; newIdx < newChildren.length; newIdx++) { 34 const newChild = newChildren[newIdx]; 35 36 // 从Map中查找匹配的Fiber 37 const matchedFiber = 38 newChild.key !== null 39 ? existingChildren.get(newChild.key) 40 : existingChildren.get(newIdx) || null; 41 42 if (matchedFiber !== null) { 43 // 找到匹配,更新并复用 44 const newFiber = useFiber(matchedFiber, newChild.props); 45 newFiber.index = newIdx; 46 // ... 47 existingChildren.delete( 48 newChild.key !== null ? newChild.key : newIdx 49 ); 50 } else { 51 // 没有找到匹配,创建新Fiber 52 // ... 53 } 54 } 55 56 // 删除Map中剩余的Fiber(不再使用的子节点) 57 existingChildren.forEach(child => deleteChild(returnFiber, child)); 58 59 return resultingFirstChild; 60}
本章深入探讨了React Fiber架构的核心机制,从Fiber节点的数据结构到工作循环的调度策略,再到协调算法的Diff策略。Fiber架构是React实现并发渲染的基础,理解这些原理对于掌握React的高级特性至关重要。
在下一章中,我们将继续探讨并发特性,包括Lane优先级模型、Suspense架构和Transitions机制。