diff算法核心函数reconcileChildren
reconcileChildren
函数是实现Virtual DOM diff算法的核心载体,主要负责在组件更新时比较新旧子元素的差异,并生成新的fiber
树
参数current代表当前组件的Fiber节点,workInProgress是正在构建的新Fiber树,nextChildren是新的子元素,renderLanes是优先级相关的车道
首次渲染时,没有current,因此直接挂载子节点,而更新时则需要协调新旧子节点
更新前:<div><span key="a"/></div>
更新后:<div><p key="b"/><span key="a"/></div>
协调过程:
1. 检测key="b"新节点 → 创建新Fiber
2. 发现key="a"节点位置变化 → 标记Placement
3. 旧span节点 → 复用并移动位置
该函数在React渲染流程中的定位
beginWork阶段 → reconcileChildren → 生成子Fiber树
↓
实现Virtual DOM diff算法的核心载体
graph TD
A[开始] --> B{current存在?}
B -->|是| C[协调更新模式]
B -->|否| D[初始挂载模式]
C --> E[调用reconcileChildFibers]
D --> F[调用mountChildFibers]
E & F --> G[返回子Fiber节点]
export function reconcileChildren(
current: Fiber | null,
workInProgress: Fiber,
nextChildren: any,
renderLanes: Lanes,
) {
if (current === null) {
// 挂载阶段处理
workInProgress.child = mountChildFibers(
workInProgress,
null,
nextChildren,
renderLanes,
);
} else {
// 更新阶段处理
workInProgress.child = reconcileChildFibers(
workInProgress,
current.child,
nextChildren,
renderLanes,
);
}
}
ChildReconciler工厂函数
这两个不同阶段的fiber处理都是调用的ChildReconciler
函数,区别是shouldTrackSideEffects
入参的区别
-
mountChildFibers -> false
-
reconcileChildFibers -> true
ChildReconciler
是个工厂函数,函数内部定义了deleteChild、deleteRemainingChildren、mapRemainingChildren等方法,还有处理文本节点、元素、片段的update函数。这些方法共同负责处理子节点的增删改,以及Fiber节点的复用和标记副作用,对外暴露了reconcileChildFibers
函数,也是个处理函数
函数reconcileChildFibers
的主要职责是协调子Fiber节点,处理不同类型的子元素,如React元素、Portal、懒加载组件、数组、迭代器等。函数接收四个参数:returnFiber(父Fiber)、currentFirstChild(当前第一个子Fiber)、newChild(新的子元素)、lanes(优先级车道)
graph TD
A[开始] --> B{是否无key Fragment?}
B -->|是| C[展开子节点]
B -->|否| D{类型判断}
D -->|React元素| E[单元素协调]
D -->|Portal| F[Portal协调]
D -->|懒加载| G[递归解析]
D -->|数组| H[数组协调]
D -->|迭代器| I[迭代器协调]
D -->|文本| J[文本节点处理]
E & F & G & H & I & J --> K[标记位置]
K --> L{是否有效节点?}
L -->|否| M[删除旧节点]
L -->|是| N[返回新Fiber树]
function ChildReconciler(shouldTrackSideEffects) {
// 核心协调方法集合:
// 1. 节点删除逻辑
function deleteChild(returnFiber, childToDelete) {
if (shouldTrackSideEffects) {
// 维护父Fiber的deletions数组
returnFiber.deletions = returnFiber.deletions || []
returnFiber.deletions.push(childToDelete)
returnFiber.flags |= ChildDeletion // 标记删除操作
}
}
// 2. 节点复用机制
function useFiber(fiber, pendingProps) {
const clone = createWorkInProgress(fiber, pendingProps)
clone.index = 0 // 重置索引
clone.sibling = null // 断开兄弟节点
return clone
}
// 3. 位置标记算法
function placeChild(newFiber, lastPlacedIndex, newIndex) {
newFiber.index = newIndex
const current = newFiber.alternate
if (current) {
// 移动判断逻辑
if (current.index < lastPlacedIndex) {
newFiber.flags |= Placement // 标记移动
return lastPlacedIndex
}
return current.index // 保持原位
} else {
newFiber.flags |= Placement // 标记插入
return lastPlacedIndex
}
}
// 4. 多种节点类型处理
const updateFunctions = {
Text: updateTextNode,
Element: updateElement,
Portal: updatePortal,
Fragment: updateFragment
}
function reconcileChildFibers(
returnFiber: Fiber,
currentFirstChild: Fiber | null,
newChild: any,
lanes: Lanes,
): Fiber | null {
// 处理无key的顶层Fragment
const isUnkeyedTopLevelFragment = ...;
if (isUnkeyedTopLevelFragment) {
newChild = newChild.props.children; // 展开Fragment子节点
}
// 核心协调逻辑分支
if (typeof newChild === 'object' && newChild !== null) {
switch (newChild.$$typeof) {
case REACT_ELEMENT_TYPE: // React元素处理
return reconcileSingleElement(...);
case REACT_PORTAL_TYPE: // Portal处理
return reconcileSinglePortal(...);
case REACT_LAZY_TYPE: // 懒加载组件处理
return reconcileChildFibers(...); // 递归解析
}
// 数组/迭代器处理
if (isArray(newChild)) return reconcileChildrenArray(...);
if (getIteratorFn(newChild)) return reconcileChildrenIterator(...);
throwOnInvalidObjectType(...); // 非法对象类型报错
}
// 文本节点处理
if (isStringOrNumber(newChild)) {
return reconcileSingleTextNode(...);
}
// 清空剩余子节点
return deleteRemainingChildren(...);
}
return reconcileChildFibers
}
下面我们就详细的看看不同种类的协调逻辑
reconcileSingleElement 协调React元素
该函数负责处理单个React元素,函数接收returnFiber、currentFirstChild、element和lanes作为参数。它的主要任务是在现有的子Fiber节点中查找是否有可以复用的节点,如果没有则创建新的Fiber节点
函数开始通过element.key进行循环查找currentFirstChild的子节点。如果找到key相同的子节点,就会检查其类型是否匹配。这里有两种情况:处理Fragment类型和其他元素类型。对于Fragment,需要验证子节点的标签是否为Fragment,然后复用现有的Fiber节点。对于其他元素类型,会检查elementType是否一致,或者是否属于热更新的兼容情况,或者是懒加载组件。如果匹配,则删除剩余的兄弟节点,并复用现有的Fiber。
如果没有找到匹配的key,则删除当前子节点,继续查找下一个兄弟节点。如果循环结束后仍未找到,就会创建新的Fiber节点。这里分Fragment和其他元素两种情况处理,分别调用不同的创建函数
最后,函数的返回值是新的或复用的Fiber节点,并正确设置其return指针,确保Fiber树的连接正确
单元素协调入口 → reconcileSingleElement → 复用或创建Fiber
↓
实现O(n)复杂度的key匹配优化算法
graph TD
A[开始] --> B{遍历子节点}
B --> C{key匹配?}
C -->|是| D{类型匹配?}
D -->|是| E[复用Fiber并返回]
D -->|否| F[删除所有子节点]
C -->|否| G[删除当前节点]
B --> H[创建新Fiber]
E & F & G & H --> I[返回新Fiber]
function reconcileSingleElement(
returnFiber: Fiber,
currentFirstChild: Fiber | null,
element: ReactElement,
lanes: Lanes,
): Fiber {
// 核心协调流程:
const key = element.key;
let child = currentFirstChild;
// 阶段1:遍历现有子节点寻找可复用节点
while (child !== null) {
if (child.key === key) { // Key匹配
const elementType = element.type;
// 处理Fragment类型
if (elementType === REACT_FRAGMENT_TYPE) {
if (child.tag === Fragment) { // Fragment类型匹配
// 删除剩余兄弟节点
deleteRemainingChildren(returnFiber, child.sibling);
// 复用现有Fiber节点
const existing = useFiber(child, element.props.children);
existing.return = returnFiber;
// 开发模式调试信息
if (__DEV__) {
existing._debugSource = element._source;
existing._debugOwner = element._owner;
}
return existing;
}
} else {
// 类型匹配检查(含热更新兼容)
if (child.elementType === elementType ||
(__DEV__ && isCompatibleFamilyForHotReloading(child, element)) ||
(isLazyComponent(elementType) && resolveLazy(elementType) === child.type)) {
// 清理后续兄弟节点
deleteRemainingChildren(returnFiber, child.sibling);
// 复用现有Fiber
const existing = useFiber(child, element.props);
existing.ref = coerceRef(returnFiber, child, element);
existing.return = returnFiber;
// 开发模式调试信息
if (__DEV__) {
existing._debugSource = element._source;
existing._debugOwner = element._owner;
}
return existing;
}
}
// Key匹配但类型不匹配 → 删除所有子节点
deleteRemainingChildren(returnFiber, child);
break;
} else {
// Key不匹配 → 删除当前子节点
deleteChild(returnFiber, child);
}
child = child.sibling;
}
// 阶段2:创建新Fiber节点
if (element.type === REACT_FRAGMENT_TYPE) {
// 创建Fragment类型的Fiber
const created = createFiberFromFragment(
element.props.children,
returnFiber.mode,
lanes,
element.key,
);
created.return = returnFiber;
return created;
} else {
// 创建普通元素类型的Fiber
const created = createFiberFromElement(element, returnFiber.mode, lanes);
created.ref = coerceRef(returnFiber, currentFirstChild, element);
created.return = returnFiber;
return created;
}
}
reconcileChildrenArray 子元素数组的更新
该函数无法进行两端优化,因为没有反向指针,所以主要采用前向遍历和映射查找的方式
函数分为几个阶段:初始遍历新旧子节点,处理剩余的新节点,以及使用映射处理移动和删除
graph TD
A[开始] --> B{阶段1顺序遍历}
B -->|匹配| C[复用节点]
B -->|不匹配| D[阶段2快速插入]
C --> E[标记位置]
E --> B
D --> F{旧节点耗尽?}
F -->|是| G[创建新节点]
F -->|否| H[阶段3映射匹配]
G --> I[标记插入]
H --> J[建立旧节点映射]
J --> K[遍历新节点]
K --> L{存在复用?}
L -->|是| M[移动节点]
L -->|否| N[创建新节点]
M --> O[更新映射表]
O --> K
function reconcileChildrenArray(
returnFiber: Fiber,
currentFirstChild: Fiber | null,
newChildren: Array<*>,
lanes: Lanes,
): Fiber | null {
// 核心流程分解为三个阶段:
// 阶段1:顺序匹配(新旧节点同步遍历)
for (; oldFiber && newIdx < newChildren.length; newIdx++) {
if (oldFiber.index > newIdx) {
// 索引跳跃处理
nextOldFiber = oldFiber;
oldFiber = null;
}
const newFiber = updateSlot(...); // 尝试复用节点
if (!newFiber) break; // 不匹配时进入下一阶段
deleteChild(...); // 清理无法复用的旧节点
placeChild(...); // 标记位置变化
}
// 阶段2:批量插入新节点(当旧节点已耗尽)
if (!oldFiber) {
for (; newIdx < newChildren.length; newIdx++) {
createChild(...); // 创建新Fiber节点
placeChild(...); // 标记插入位置
}
}
// 阶段3:映射匹配剩余节点(处理移动/删除操作)
const existingChildren = mapRemainingChildren(...);
for (; newIdx < newChildren.length; newIdx++) {
updateFromMap(...); // 从映射表查找可复用节点
existingChildren.delete(...); // 移除已匹配节点
placeChild(...); // 更新位置标记
}
existingChildren.forEach(...); // 删除未匹配的旧节点
}
completeUnitOfWork
completeUnitOfWork
函数属于工作循环的一部分,负责完成当前工作单元并处理可能的错误和兄弟节点
函数completeUnitOfWork
接收一个unitOfWork
参数,进入一个do-while循环,处理当前的工作单元。代码分为两部分:正常完成的情况和出现错误的情况
在正常流程中,函数调用completeWork
来处理当前Fiber节点,如果返回了新的工作(next
不为null),则立即处理。在错误情况下,调用unwindWork
进行回滚,并处理可能的错误边界
graph TD
A[开始] --> B{当前节点是否正常完成?}
B -->|是| C[执行completeWork收集副作用]
B -->|否| D[执行unwindWork错误处理]
C --> E{生成新工作节点?}
E -->|是| F[立即处理新节点]
D --> G{生成新工作节点?}
G -->|是| H[处理新节点]
F & H --> I[处理兄弟节点]
I --> J{有兄弟节点?}
J -->|是| K[处理兄弟节点]
J -->|否| L[回溯父节点]
L --> M{是否到达根节点?}
M -->|是| N[标记根完成]
function completeUnitOfWork(unitOfWork: Fiber): void {
let completedWork = unitOfWork; // 初始化当前工作单元
do {
const current = completedWork.alternate; // 获取已提交的对应Fiber节点
const returnFiber = completedWork.return; // 获取父Fiber节点
// 检查节点是否正常完成(无错误标志)
if ((completedWork.flags & Incomplete) === NoFlags) {
setCurrentDebugFiberInDEV(completedWork); // 开发环境设置调试标记
let next;
if (!enableProfilerTimer || (completedWork.mode & ProfileMode) === NoMode) {
next = completeWork(current, completedWork, subtreeRenderLanes); // 非性能分析模式
} else {
startProfilerTimer(completedWork); // 启动性能分析计时器
next = completeWork(current, completedWork, subtreeRenderLanes);
stopProfilerTimerIfRunningAndRecordDelta(completedWork, false); // 停止并记录耗时
}
resetCurrentDebugFiberInDEV(); // 重置调试标记
if (next !== null) { // 如果生成新工作单元
workInProgress = next; // 更新全局工作指针
return; // 优先处理新任务
}
} else {
// 错误处理流程
const next = unwindWork(current, completedWork, subtreeRenderLanes); // 执行错误边界处理
if (next !== null) {
next.flags &= HostEffectMask; // 保留宿主环境相关副作用
workInProgress = next; // 更新工作指针继续处理
return;
}
// 性能分析相关错误处理
if (enableProfilerTimer && (completedWork.mode & ProfileMode) !== NoMode) {
let actualDuration = completedWork.actualDuration;
let child = completedWork.child; // 累加所有子节点耗时
while (child !== null) {
actualDuration += child.actualDuration;
child = child.sibling;
}
completedWork.actualDuration = actualDuration; // 更新总耗时
}
if (returnFiber !== null) {
returnFiber.flags |= Incomplete; // 标记父节点未完成
returnFiber.subtreeFlags = NoFlags; // 清空子树标志
returnFiber.deletions = null; // 重置删除队列
} else { // 已回溯到根节点
workInProgressRootExitStatus = RootDidNotComplete; // 设置根状态
workInProgress = null;
return;
}
}
const siblingFiber = completedWork.sibling; // 获取兄弟节点
if (siblingFiber !== null) {
workInProgress = siblingFiber; // 优先处理兄弟节点
return;
}
// 回溯至父节点
completedWork = returnFiber;
workInProgress = completedWork; // 更新工作指针
} while (completedWork !== null); // 循环直到根节点
// 完成整个根节点的处理
if (workInProgressRootExitStatus === RootInProgress) {
workInProgressRootExitStatus = RootCompleted; // 更新根状态为完成
}
}
completeWork
该函数在React渲染流程中的作用定位
协调阶段 → beginWork → completeWork
↓
完成DOM准备/属性收集/副作用标记
函数completeWork
根据不同的Fiber类型处理各节点的完成工作,比如HostRoot、HostComponent、HostText等。每个case处理不同类型的组件,如类组件、函数组件、Host组件等
graph TD
A[开始] --> B{当前Fiber类型}
B -->|HostRoot| C[处理根节点状态]
B -->|HostComponent| D[创建/更新DOM节点]
B -->|HostText| E[处理文本更新]
B -->|Suspense| F[处理挂起状态]
C & D & E & F --> G[属性冒泡]
G --> H[返回null继续遍历]
function completeWork(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
): Fiber | null {
// 核心处理逻辑分三个阶段:
// 1. 上下文管理 - 弹出当前Fiber的树上下文
popTreeContext(workInProgress);
// 2. 分类型处理(核心switch结构)
switch (workInProgress.tag) {
// 函数组件/类组件等通用处理
case FunctionComponent:
case ClassComponent: {
// 处理旧版上下文
if (isLegacyContextProvider(Component)) {
popLegacyContext(workInProgress);
}
// 属性冒泡机制
bubbleProperties(workInProgress);
return null;
}
// 根节点处理
case HostRoot: {
// 处理过渡追踪(Transition Tracing)
if (enableTransitionTracing) {
const transitions = getWorkInProgressTransitions();
if (transitions !== null) {
workInProgress.flags |= Passive; // 标记被动效果
}
}
// 缓存处理逻辑
if (enableCache) {
// 比较新旧缓存差异
if (cache !== previousCache) {
workInProgress.flags |= Passive;
}
popCacheProvider(workInProgress, cache);
}
// 处理hydration状态
const wasHydrated = popHydrationState(workInProgress);
if (wasHydrated) {
markUpdate(workInProgress); // 标记需要更新
}
}
// DOM元素处理
case HostComponent: {
// 创建/更新DOM实例
const instance = createInstance(
type,
newProps,
rootContainerInstance,
currentHostContext,
workInProgress,
);
// 子节点追加策略
appendAllChildren(instance, workInProgress, false, false);
// 初始化DOM属性
if (finalizeInitialChildren(...)) {
markUpdate(workInProgress);
}
// Ref处理
if (workInProgress.ref !== null) {
markRef(workInProgress);
}
}
// 文本节点处理
case HostText: {
// 文本内容对比更新
if (oldText !== newText) {
workInProgress.stateNode = createTextInstance(...);
markUpdate(workInProgress);
}
}
// Suspense组件处理
case SuspenseComponent: {
// 脱水边界处理
if (current?.memoizedState?.dehydrated) {
const fallthrough = completeDehydratedSuspenseBoundary(...);
if (!fallthrough) return workInProgress;
}
// 挂起状态切换处理
if (nextDidTimeout !== prevDidTimeout) {
// 可见性状态更新
offscreenFiber.flags |= Visibility;
// 并发模式下的渲染控制
renderDidSuspendDelayIfPossible();
}
}
}
// 3. 通用属性冒泡(所有类型最终调用)
bubbleProperties(workInProgress);
}
renderRootSync 同步渲染
- 无任务分片(与并发模式 workLoopConcurrent 对比)
- 无时间切片( shouldYield 始终返回false)
- 无优先级中断机制
更新调度 → renderRootSync → workLoopSync → commitRoot
↓
完成同步模式下的Fiber树协调
graph TD
A[开始] --> B[保存上下文]
B --> C{是否需新工作栈?}
C -->|是| D[初始化工作栈]
C -->|否| E[进入渲染循环]
D --> E
E --> F[执行workLoopSync]
F --> G{是否报错?}
G -->|是| H[处理错误]
G -->|否| I[清理资源]
H --> E
I --> J[完整性检查]
J --> K[返回结果状态]
function renderRootSync(root: FiberRoot, lanes: Lanes) {
// 保存当前执行上下文和Dispatcher
const prevExecutionContext = executionContext;
executionContext |= RenderContext; // 标记进入渲染阶段
const prevDispatcher = pushDispatcher(); // 切换当前Dispatcher
// 初始化工作堆栈
if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {
if (enableUpdaterTracking) { // 开发工具跟踪逻辑
if (isDevToolsPresent) {
// 处理更新队列缓存
const memoizedUpdaters = root.memoizedUpdaters;
if (memoizedUpdaters.size > 0) {
restorePendingUpdaters(root, workInProgressRootRenderLanes);
memoizedUpdaters.clear();
}
// 移动待处理Fiber到缓存
movePendingFibersToMemoized(root, lanes);
}
}
workInProgressTransitions = getTransitionsForLanes(root, lanes); // 获取过渡追踪
prepareFreshStack(root, lanes); // 准备新工作栈
}
// 开发模式调试标记
if (__DEV__) {
if (enableDebugTracing) logRenderStarted(lanes);
}
// 性能分析标记
if (enableSchedulingProfiler) markRenderStarted(lanes);
// 核心渲染循环
do {
try {
workLoopSync(); // 同步工作循环
break;
} catch (thrownValue) {
handleError(root, thrownValue); // 错误处理
}
} while (true);
// 清理阶段
resetContextDependencies(); // 重置上下文依赖
executionContext = prevExecutionContext; // 恢复执行上下文
popDispatcher(prevDispatcher); // 恢复原始Dispatcher
// 完整性检查
if (workInProgress !== null) {
throw new Error('Cannot commit an incomplete root');
}
// 开发模式结束标记
if (__DEV__ && enableDebugTracing) logRenderStopped();
if (enableSchedulingProfiler) markRenderStopped();
// 重置全局状态
workInProgressRoot = null;
workInProgressRootRenderLanes = NoLanes;
return workInProgressRootExitStatus; // 返回渲染结果状态
}
workLoopSync函数实现,与异步相比,少了shouldYield
的判断
function workLoopSync() {
// Already timed out, so perform work without checking if we need to yield.
while (workInProgress !== null) {
performUnitOfWork(workInProgress);
}
}