从首次渲染了解React Fiber架构
关键全局变量
FiberRootNode相关:
export type RootTag = 0 | 1 | 2;
export const LegacyRoot = 0;
export const BlockingRoot = 1;
export const ConcurrentRoot = 2;
复制代码
Mode FiberRootNode属性tag的可能值
export type TypeOfMode = number;
export const NoMode = 0b00000; // 如创建RootFiber时使用此mod
export const StrictMode = 0b00001;
// TODO: Remove BlockingMode and ConcurrentMode by reading from the root
// tag instead
export const BlockingMode = 0b00010;
export const ConcurrentMode = 0b00100;
export const ProfileMode = 0b01000; // 开发模式下创建RootFiber时使用此mod
export const DebugTracingMode = 0b10000;
复制代码
update与reconcile
// The root we're working on
let workInProgressRoot: FiberRoot | null = null;
// The fiber we're working on
let workInProgress: Fiber | null = null;
// The lanes we're rendering
let workInProgressRootRenderLanes: Lanes = NoLanes;
复制代码
优先级以及lane
// LanePriority越大优先级越高
export const SyncLanePriority: LanePriority = 15;
export const SyncBatchedLanePriority: LanePriority = 14;
const InputDiscreteHydrationLanePriority: LanePriority = 13;
export const InputDiscreteLanePriority: LanePriority = 12;
const InputContinuousHydrationLanePriority: LanePriority = 11;
export const InputContinuousLanePriority: LanePriority = 10;
const DefaultHydrationLanePriority: LanePriority = 9;
export const DefaultLanePriority: LanePriority = 8;
const TransitionHydrationPriority: LanePriority = 7;
export const TransitionPriority: LanePriority = 6;
const RetryLanePriority: LanePriority = 5;
const SelectiveHydrationLanePriority: LanePriority = 4;
const IdleHydrationLanePriority: LanePriority = 3;
const IdleLanePriority: LanePriority = 2;
const OffscreenLanePriority: LanePriority = 1;
export const NoLanePriority: LanePriority = 0;
// 对应LanePriority,Lane越小优先级越高,优先级使用二进制展示可以更直白的展示当前所拥有的权限,且更方便权限合并与提取
const TotalLanes = 31;
export const NoLanes: Lanes = /* */ 0b0000000000000000000000000000000;
export const NoLane: Lane = /* */ 0b0000000000000000000000000000000;
export const SyncLane: Lane = /* */ 0b0000000000000000000000000000001;
export const SyncBatchedLane: Lane = /* */ 0b0000000000000000000000000000010;
export const InputDiscreteHydrationLane: Lane = /* */ 0b0000000000000000000000000000100;
const InputDiscreteLanes: Lanes = /* */ 0b0000000000000000000000000011000;
const InputContinuousHydrationLane: Lane = /* */ 0b0000000000000000000000000100000;
const InputContinuousLanes: Lanes = /* */ 0b0000000000000000000000011000000;
export const DefaultHydrationLane: Lane = /* */ 0b0000000000000000000000100000000;
export const DefaultLanes: Lanes = /* */ 0b0000000000000000000111000000000;
const TransitionHydrationLane: Lane = /* */ 0b0000000000000000001000000000000;
const TransitionLanes: Lanes = /* */ 0b0000000001111111110000000000000;
const RetryLanes: Lanes = /* */ 0b0000011110000000000000000000000;
export const SomeRetryLane: Lanes = /* */ 0b0000010000000000000000000000000;
export const SelectiveHydrationLane: Lane = /* */ 0b0000100000000000000000000000000;
const NonIdleLanes = /* */ 0b0000111111111111111111111111111;
export const IdleHydrationLane: Lane = /* */ 0b0001000000000000000000000000000;
const IdleLanes: Lanes = /* */ 0b0110000000000000000000000000000;
export const OffscreenLane: Lane = /* */ 0b1000000000000000000000000000000;
export const NoTimestamp = -1;
let currentUpdateLanePriority: LanePriority = NoLanePriority;
复制代码
state更新方式
export const UpdateState = 0;
export const ReplaceState = 1;
export const ForceUpdate = 2;
export const CaptureUpdate = 3;
复制代码
FiberNode tag属性的可能值
export type WorkTag =
| 0
| 1
| 2
| 3
| 4
| 5
| 6
| 7
| 8
| 9
| 10
| 11
| 12
| 13
| 14
| 15
| 16
| 17
| 18
| 19
| 20
| 21
| 22
| 23
| 24;
export const FunctionComponent = 0;
export const ClassComponent = 1;
export const IndeterminateComponent = 2; // Before we know whether it is function or class
export const HostRoot = 3; // Root of a host tree. Could be nested inside another node.
export const HostPortal = 4; // A subtree. Could be an entry point to a different renderer.
export const HostComponent = 5;
export const HostText = 6;
export const Fragment = 7;
export const Mode = 8;
export const ContextConsumer = 9;
export const ContextProvider = 10;
export const ForwardRef = 11;
export const Profiler = 12;
export const SuspenseComponent = 13;
export const MemoComponent = 14;
export const SimpleMemoComponent = 15;
export const LazyComponent = 16;
export const IncompleteClassComponent = 17;
export const DehydratedFragment = 18;
export const SuspenseListComponent = 19;
export const FundamentalComponent = 20;
export const ScopeComponent = 21;
export const Block = 22;
export const OffscreenComponent = 23;
export const LegacyHiddenComponent = 24;
复制代码
FiberNode
function FiberNode(
tag: WorkTag,
pendingProps: mixed,
key: null | string,
mode: TypeOfMode,
) {
// Instance
this.tag = tag;
this.key = key;
this.elementType = null;
this.type = null;
this.stateNode = null;
// Fiber tree 链表
this.return = null;
this.child = null;
this.sibling = null;
this.index = 0;
this.ref = null;
// 组件属性数据
this.pendingProps = pendingProps;
this.memoizedProps = null;
this.updateQueue = null; // 更新队列,队列内放着即将要发生的变更状态
this.memoizedState = null;
this.dependencies = null;
this.mode = mode;
// Effects
this.flags = NoFlags;
this.nextEffect = null;
this.firstEffect = null;
this.lastEffect = null;
this.lanes = NoLanes;
this.childLanes = NoLanes;
this.alternate = null; // FiberNode,双缓冲之一
}
复制代码
FiberRootNode
function FiberRootNode(containerInfo, tag, hydrate) {
this.tag = tag;
this.containerInfo = containerInfo; // DOM容器,把整个React元素渲染到这个DOM内部
this.pendingChildren = null;
this.current = null; // (HostRoot)FiberNode,指向当前已经完成的Fiber Tree 的Root
this.pingCache = null;
this.finishedWork = null; // (HostRoot)FiberNode|null,指向当前已经完成准备工作即将真实渲染的Fiber Tree Root
this.timeoutHandle = noTimeout;
this.context = null;
this.pendingContext = null;
this.hydrate = hydrate;
this.callbackNode = null;
this.callbackPriority = NoLanePriority;
this.eventTimes = createLaneMap(NoLanes); // 更新时间数组,31位的数组
this.expirationTimes = createLaneMap(NoTimestamp); // 过期时间数组,31位的数组
this.pendingLanes = NoLanes;
this.suspendedLanes = NoLanes;
this.pingedLanes = NoLanes;
this.expiredLanes = NoLanes;
this.mutableReadLanes = NoLanes;
this.finishedLanes = NoLanes;
this.entangledLanes = NoLanes;
this.entanglements = createLaneMap(NoLanes); // 纠缠数组,31位的数组,如:MutableSource
}
复制代码
从组装数据到任务调度的主线方法调用
legacyRenderSubtreeIntoContainer => createRootFiber => FiberRootNode => initialUpdateQueue => updateContainer => createUpdate => scheduleUpdateOnFiber => renderRootSync => workLoopSync => performUnitOfWork =>
复制代码
创建 ReactRoot、FiberRoot、(HostRoot)FiberNode,建立他们与 DOMContainer 的关系
legacyRenderSubtreeIntoContainer
- 根据container创建初始FiberRoot:root = container._reactRootContainer = legacyCreateRootFromDOMContainer(container, forceHydrate = false)
- fiberRoot = root._internalRoot;
- 拿到创建的FiberRoot开始处理相关 // Initial mount should not be batched
- updateContainer开始为调度作准备;
创建FiberRoot createFiberRoot函数:
创建root: FiberRootNode
:
- 将
root.current
赋值为createHostRootFiber(tag = NoMode)
所创建的FiberNode
(root.current.mode = NoMode,在开发环境下会被赋值为ProfileMode)称之为RootFiber; - 将
root.current.stateNode
赋值为root
; - 返回root,至此FiberRootNode创建成功;
createRootImpl方法中的markContainerAsRoot
将container加上上面创建的FiberNode
markContainerAsRoot(root.current, container)将conrainer添加属性[internalContainerInstanceKey] = root.current
requestEventTime获取当前执行事件的时间戳,全局变量
function requestEventTime() {
if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
// We're inside React, so it's fine to read the actual time.
return now();
} // We're not inside React, so we may be in the middle of a browser event.
if (currentEventTime !== NoTimestamp) {
// Use the same start time for all updates until we enter React again.
return currentEventTime;
} // This is the first update since React yielded. Compute a new start time.
currentEventTime = now();
return currentEventTime;
}
复制代码
初始化(HostRoot)FiberNode的UpdateQueue
updateContainer开始调度相关的工作
- 获取当前执行时间;
- requestUpdateLane,根据FiberNode的mod值计算获取优先级;
- createUpdate(eventTime, lane),根据入参初始化一个update返回,如下:
return update = {
eventTime: eventTime, // update事件的时间,当前时间戳
lane: lane, // lane优先级相关, 1
tag: UpdateState, // 普通的更新方式
payload: null,
callback: null,
next: null
};
复制代码
- update的内容设置为render时传入的element,后续会根据element来生成fiber tree如下:
update.payload = {
element: element
};
复制代码
- enqueueUpdate(container.current, update), 此处为第一个update的下一个更新指向当前update(update.next = update;)=> container.current.updateQueue.shared.pending指向update
updateQueue = {
baseState: element, // 首次update赋值为当前需要执行任务的element
firstBaseUpdate: null, // 队列中的第一个`Update`
lastBaseUpdate: null, // 队列中的最后一个`Update`
shared: {
pending: null // 准备要执行的update任务
},
effects: null // 副作用
}
复制代码
- scheduleUpdateOnFiber(container.current, lane, eventTime);
- markUpdateLaneFromFiberToRoot(fiber, lane),给fiber属性lanes赋值
- markRootUpdated(root, updateLane, eventTime)
root.pendingLanes |= updateLane; // 即将执行的lanes
const higherPriorityLanes = updateLane - 1; // 计算比当前lane更高优先级的lanes
// 取消同等或较低优先级的更新。
root.suspendedLanes &= higherPriorityLanes; // 0
root.pingedLanes &= higherPriorityLanes; // 0
const index = laneToIndex(updateLane); // 根据updateLane获取index,0
eventTimes[index] = eventTime; // 更新触发更新的时间,第一位设置了时间值
复制代码
- performSyncWorkOnRoot 开始执行同步任务在root, 处理更新
lanes = getNextLanes(root, NoLanes);; // 根据root fiber挂载的updateQueue获取下个要执行任务的lanes
exitStatus = renderRootSync(root, lanes = 1); // 同步render root
复制代码
- renderRootSync
/**
全局变量赋值备用
workInProgressRoot = root;
workInProgress = createWorkInProgress(root.current, null); 给全局变量workInProgress赋值为当前fiber节点的副本
workInProgressRootRenderLanes = subtreeRenderLanes = workInProgressRootIncludedLanes = lanes;
workInProgressRootExitStatus = RootIncomplete;
workInProgressRootFatalError = null;
workInProgressRootSkippedLanes = NoLanes;
workInProgressRootUpdatedLanes = NoLanes;
workInProgressRootPingedLanes = NoLanes;
*/
prepareFreshStack(root, lanes); // 准备新的任务栈相关
/*
performUnitOfWork 方法循环调用 beginWork 找到处理对应组件的 hander 方法
采用深度优先(先序优先)的方式遍历创建子节点,遇到同级节点下有多个子节点时,会为每
个节点创建一个 sibling 属性指向下一个同级节点
当遍历到某个分支的最深节点(没有子节点)时调用 completeUnitOfWork 方法
completeUnitOfWork 方法判断如果有 sibling(下一个同级节点)则返回给 performUnitOfWork
没有 sibling 则寻找 return(父节点),如果父节点有 sibling 继续返回给 performUnitOfWork
最终 return 为 null,代表所有遍历都已完成
从而实现深度优先遍历去创建整个 Fiber tree
while (workInProgress !== null) {
performUnitOfWork(workInProgress);
}
*/
workLoopSync();
复制代码
// 全局变量
export let current: Fiber | null = null; // 当前正在执行任务的fiber
export let isRendering: boolean = false; // 是否正在render
const valueStack: Array<any> = []; //
let fiberStack: Array<Fiber | null>; // FiberNode栈
复制代码
performUnitOfWork(unitOfWork: FiberNode)
var current = unitOfWork.alternate;
/*
全局变量赋值
current = fiber;
isRendering = false;
*/
setCurrentFiber(unitOfWork);
next = beginWork(current, unitOfWork, subtreeRenderLanes); // 拿到下一个update queue.next, 并且clone当前current.updateQueue到workInProgress,workInProgress是实际用来执行操作的Fiber tree,如此则不会影响current
// 主线执行方法调用
beginWork => updateHostRoot => processUpdateQueue => reconcileChildren => reconcileChildFibers => reconcileSingleElement => createFiberFromElement => completeUnitWork => completeWork => createInstance => createElement => finalizeInitialChildren
复制代码
-
updateHostRoot
深度优先遍历并按照updateQueue处理update更新对应的fiber
-
processUpdateQueue 处理更新队列,包括state(目前只有element)、重新赋值第一个update、最后一个update、将update的指向改为update.next循环处理直到pendingQueue为空则处理完成;
-
reconcileChildren 协调子节点
Fiber Reconciler 在执行过程中,会分为 2 个阶段。
- 阶段一,通过element生成 Fiber 树,得出需要更新的节点信息。这一步是一个渐进的过程,可以被打断。
- 阶段二,将需要更新的节点一次过批量更新,这个过程不能被打断。
深度优先处理
-
reconcileSingleElement 协调单个子节点元素
-
当key相同和对应类型符合时分fragment, protal和其他情况,进行复用,并删除剩余节点。 key符合, 当类型不匹配时,删除当前节点;
-
key和type不匹配时,删除当前fiber。 在新增时,分frament 和其他情况创建fiber。
-
-
createFiberFromElement 根据element创建新的fiber构建最终完整的携带所有更新信息fiber tree
-
-
completeUnitOfWork
判断如果有 sibling(下一个同级节点)则返回给 performUnitOfWork
没有 sibling 则寻找 return(父节点),如果父节点有 sibling 继续返回给 performUnitOfWork,从而实现深度优先遍历去创建整个 Fiber tree。
-
completeWork 通过调用createInstance => createElement => finalizeInitialChildren来创建真实dom并添加properties,
-
最后commitRoot添加到container,即div#root完成整个初次渲染。
总结
- 创建初始FiberRootNode
- 创建初始rootFiber并初始化updateQueue
- 根据element创建update并赋值给updateQueue
- 根据updateQueue复制FiberRoot.current到全局变量workInProgress并进行fiber tree调协生成最新的fiber tree
- 根据workInProgress循环创建最新dom树
- 将dom树添加到container
// 整体主线方法调用
legacyRenderSubtreeIntoContainer => createRootFiber => FiberRootNode => initialUpdateQueue => updateContainer => createUpdate => scheduleUpdateOnFiber => renderRootSync => workLoopSync => performUnitOfWork => beginWork => updateHostRoot => processUpdateQueue => reconcileChildren => reconcileChildFibers => reconcileSingleElement => createFiberFromElement => completeUnitWork => completeWork => createInstance => createElement => finalizeInitialChildren => commitRoot
复制代码