React源码系列3---Render阶段
Fiber节点如何被创建?
如何构建一棵Fiber树?
render 流程
render阶段开始于performSyncWorkOnRoot或者performConcurrentWorkOnRoot的方法调用。具体的调用方法取决与是同步还是异步的
performSyncWorkOnRoot:
// performSyncWorkOnRoot同步调用会执行这个方法
function workLoopSync() {
while(workInProgress !== null) {
// do something
performUnitOfWork(workInProgress)
}
}
//performConcurrentWorkOnRoot异步调用执行
function workLoopConcurrent() {
while(workInProgress !== null && !shouldYield()) {
performUnitOfWork(workInProgress)
}
}
- 同步调用和异步调用的区别在于是否调用
shouldYield(),用于判断当前关键帧是否还有剩余时间;如果当前浏览器帧没有剩余时间的话,shouldYield则会中止循环过程,直到浏览器有剩余时间 workInProgress代表当前已经创建的workInProgress FiberpreformUnitOfWork方法会创建下一个Fiber节点并赋值给wornInProgress,并将workInProgress与已经创建的Fiber 节点连接起来构成Fiber 树Fiber Reconciler来自于Stack Reconciler,通过遍历的方式实现可以中断的递归- workLoopConcurrent调用的主要方法就是performUnitOfWork,
performUnitOfWork也因此分为两个阶段
'递'
- 从
rootFiber开始,向下,深度优先遍历(栈结构)。为遍历到的每一个Fiber 节点调用beginWork - beginWork会根据传入的
Fiber 节点创建一个子Fiber节点,并将这两个节点链接起来 - 当遍历到子节点的时候就会进行到
归的阶段
'归'
- 在这一阶段会调用completeWork来处理
Fiber节点 - 当某个
Fiber节点执行完completeWork,如果存在其兄弟节点(fiber.sibling !== null),将会进入兄弟Fiber的‘递’阶段 - 递****归的过程会交错执行,一直到归到最终的
rootFiber。最后,render阶段结束
Example:
使用上一节用到的FunctionComponent:
function App() {
return (
<div>
hello
<span>React</span>
</div>
)
}
Fiber树结构如下:

render阶段执行过程:
- rootFiber beginWork
- App Fiber beginWork
- div Fiber beginWork
- "Hello" Fiber beginWork
- "Hello" Fiber completeWork
- span Fiber beginWork
7. "React" Fiber beginWork // React会优化单一文本节点,所以并不会出现
8. "React" Fiber completeWork- span Fiber completeWork
- div Fiber completeWork
- App Fiber completeWork
- rootFiber completeWork
performUnitOfWork递归写法:
function performUnitOfWork(fiber) {
// beginWork()
if (fiber.child) {
performUnitOfWork(fiber.child)
}
// completeWork
if (fiber.sibling) {
performUnitOfWork(fiber.sibling)
}
}
beginWork
beginWork的工作是传入当前的fiber 节点,创建子Fiber节点
function beginWork(
current: Fiber | null, //
workInProgress: Fiber,
renderLanes: Lanes
): Fiber | null {
// do something
}
参数:
- current: 当前组件对应的
Fiber节点在上一次更新时候的Fiber----workInProgress.alternate - workInProgress: 当前组件对应的
Fiber节点 - renderLanes: 优先级
在双缓存机制这一块有提到,组件
mount的时候,因为是在首屏渲染,此时页面并没有挂载DOM,是不存在当前组件对应的Fiber 节点在上次更新时的Fiber 节点的,也就是说,在mount的时候current === null
current === null来判断组件当前是在
mount还是update
基于组件所处的不同状态(mount和update),beginWork可以分为两个部分:
mount时: 除fiberRootNode之外,current === null。会根据fiber.tag的不同,创建不同的子Fiber节点update时:如果current存在,在满足条件的时候可以复用current节点,克隆current.child作为workInProgress.child,而不需要新建一个workInProgress.child
function beginWork(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes
): Fiber | null {
// update时:如果current存在可能存在优化路径,可以复用current(即上一次更新的Fiber节点)
if (current !== null) {
// ...省略
// 复用current
return bailoutOnAlreadyFinishedWork(
current,
workInProgress,
renderLanes,
);
} else {
didReceiveUpdate = false;
}
// mount时:根据tag不同,创建不同的子Fiber节点
switch (workInProgress.tag) {
case IndeterminateComponent:
// ...省略
case LazyComponent:
// ...省略
case FunctionComponent:
// ...省略
case ClassComponent:
// ...省略
case HostRoot:
// ...省略
case HostComponent:
// ...省略
case HostText:
// ...省略
// ...省略其他类型
}
}
update
在update的时候可以直接复用前一次更新的子Fiber,不需要新建子Fiber的情况,即didReceiveUpdate === false
if (current !== null) {
const oldProps = current.memoizedProps;
const newProps = workInProgress.pendingProps;
if (
oldProps !== newProps ||
hasLegacyContextChanged() ||
(__DEV__ ? workInProgress.type !== current.type : false)
) {
didReceiveUpdate = true;
} else if (!includesSomeLane(renderLanes, updateLanes)) {
didReceiveUpdate = false;
switch (workInProgress.tag) {
// 省略处理
}
return bailoutOnAlreadyFinishedWork(
current,
workInProgress,
renderLanes,
);
} else {
didReceiveUpdate = false;
}
} else {
didReceiveUpdate = false;
}
- 当
oldProps === newProps && workInProgress.type === current.type,也就是在type和props不变的情况下 !includesSomeLane(renderLanes, updateLanes),当前的Fiber节点优先级较低的时候
mount
当不满足优化的路径的时候,就会去新建子Fiber
根据fiber.tag的不同,进入不同类型的Fiber去创建逻辑
// mount时:根据tag不同,创建不同的Fiber节点
switch (workInProgress.tag) {
case IndeterminateComponent:
// ...省略
case LazyComponent:
// ...省略
case FunctionComponent:
// ...省略
return updateFunctionComponent(
current,
workInProgress,
Component,
resolvedProps,
renderLanes,
)
case ClassComponent:
// ...省略
return updateClassComponent(
current,
workInProgress,
Component,
resolvedProps,
renderLanes,
)
case HostRoot:
// ...省略
case HostComponent:
return updateHostComponent(current, workInProgress, renderLanes);
case HostText:
// ...省略
case SuspenseComponent:
// ...省略
case HostPortal:
// ...省略
case ForwardRef:
// ...省略
case Fragment:
// ...省略
case Mode:
// ...省略
case Profiler:
// ...省略
case ContextProvider:
// ...省略
case ContextConsumer:
// ...省略
// ...省略其他类型
}
React组件类型TagType
对于常见的组件类型: FunctionComponent/ClassComponent/HostComponent,他们分别调用了updateFunctionComponent/updateClassComponent/updateHostComponent,根据源码找到最终调用了reconcileChildren
reconcilerChilren
作为Reconciler模块的核心方法:
- 对于
mount的组件,他会创建一个新的子Fiber节点 - 对于
update的组件,他会将当前组件与该组件上次更新时对应时Fiber 节点比较(diff算法),将比较的结果生成新的Fiber节点
function reconcileChildren(
current: Fiber | null,
workInProgress: Fiber,
nextChildren: any,
renderLanes: Lanes,
) {
if (current === null) {
// If this is a fresh new component that hasn't been rendered yet, we
// won't update its child set by applying minimal side-effects. Instead,
// we will add them all to the child before it gets rendered. That means
// we can optimize this reconciliation pass by not tracking side-effects.
workInProgress.child = mountChildFibers(
workInProgress,
null,
nextChildren,
renderLanes,
);
} else {
// If the current child is the same as the work in progress, it means that
// we haven't yet started any work on these children. Therefore, we use
// the clone algorithm to create a copy of all the current children.
// If we had any progressed work already, that is invalid at this point so
// let's throw it out.
workInProgress.child = reconcileChildFibers(
workInProgress,
current.child,
nextChildren,
renderLanes,
);
}
}
mountChildFibers和reconcileChildFibers都调用了同一个方法ChildReconciler,唯一不同的一点是reconcileChildFibers会为生成的Fiber节点带上flags属性,而mountChildFibers不会
最终他会生成新的子Fiber节点赋值给workInProgress.child,作为本次beginWork的返回值return workInProgress.child,并作为下次preformUnitOfWork下次执行的workInProgress = next传参
flags
- 因为Renderer阶段是在内存中进行的,当工作结束之后会通知
Renderer需要执行的DOM操作; - 需要执行的操作类型就是
flags
beginWork流程图:

completeWork
组件执行
beginWork之后会创建子Fiber节点,节点上面可能存在flags属性
completeWork
和beginWork类似,completeWork也是根据不同的类型fiber.tag处理不同的逻辑
function completeWork(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes
): Fiber | null {
const newProps = workInProgress.pendingProps;
switch (workInProgress.tag) {
case IndeterminateComponent:
case LazyComponent:
case SimpleMemoComponent:
case FunctionComponent:
case ForwardRef:
case Fragment:
case Mode:
case Profiler:
case ContextConsumer:
case MemoComponent:
return null;
case ClassComponent: {
// ...省略
return null;
}
case HostRoot: {
// ...省略
updateHostContainer(workInProgress);
return null;
}
case HostComponent: {
// ...省略
return null;
}
}
// ...省略
}
- 首先看一下与页面
DOM渲染相关的Fiber节点----HostComponent
HostComponet
与上面所说的beginWork类似的是,我们同样根据current === null 判断当前页面是mount还是update,同时需要注意的是在update的时候Fiber节点是否存在对应的DOM节点,即workInProgress.stateNode !== null
case HostComponent: {
popHostContext(workInProgress)
const rootContainerInstance = getRootHostContainer()
const type = workInPropgress.type
if (current !== null && workInProgress.stateNode !== null) {
// update,---> ## update
} else {
// mount
}
return null
}
update
当update的时候,Fiber节点已经存在对应的DOM节点,不需要生成DOM;主要工作在于处理props: 事件回调,style, children...
if (current !== null && workInProgress.stateNode !== null) {
//update
updateHostComponent(
current,
workInProgress,
type,
newProps,
rootContainerInstance
)
}
const updatePayload = prepareUpdate(
instance,
type,
oldProps,
newProps,
rootContainerInstance,
currentHostContext,
);
// TODO: Type this specific to this type of component.
workInProgress.updateQueue = (updatePayload: any);
在updateHostComponent内部props处理完了之后,会赋值给workInProgress.updateQueue = (updatePayload: any),并在最终commit阶段进行渲染
mount
在mount的时候主要逻辑:
- 为
Fiber节点生成对应的DOM节点 - 将
子节点插入生成的DOM节点中 - 处理
props
const currentHostContext = getHostContext();
// 为fiber创建对应DOM节点
const instance = createInstance(
type,
newProps,
rootContainerInstance,
currentHostContext,
workInProgress,
);
// 将子孙DOM节点插入刚生成的DOM节点中
appendAllChildren(instance, workInProgress, false, false);
// DOM节点赋值给fiber.stateNode
workInProgress.stateNode = instance;
// 与update逻辑中的updateHostComponent类似的处理props的过程
if (
finalizeInitialChildren(
instance,
type,
newProps,
rootContainerInstance,
currentHostContext,
)
) {
markUpdate(workInProgress);
}
mount时只会在rootFiber存在PlaceMent``flags;
因为completeWork是处于归阶段调用的方法,每次调用appendAllChildren的时候,都会将已经生成的子DOM节点插入到当前的已经生成的DOM节点下。当归阶段执行到rootFiber时,我们已经可以得到一个构建好的DOM树
流程结束
至此,render阶段全部工作完成。在performSyncWorkOnRoot函数中fiberRootNode被传递给commitRoot方法,开启commit阶段工作流程。commitRoot
commitRoot(root)
complete 流程图
