主要步骤:创建 root --> 渲染 root --> 提交 root
createFiberRoot
创建一个FiberRoot
节点,通过FiberNode
创建一个RootFiber
节点,作为root
节点相互引用RootFiber
上function App() {return <div>hello</div>}ReactDOM.render(<App />, document.getElementById("root"))
root
节点React 首先会生成一个 root 节点,里面包含了RootFiber
和FiberRoot
,它们是互相引用的关系,可以通过对象找到对方
FiberTree
中根节点主要是靠createFiberRoot
生成的,React 在 createFiberRoot 之前会做一系列的准备工作
ReactDOM. render --> legacyRenderSubtreeIntoContainer --> container._reactRootContainer = legacyCreateRootFromDOMContainer = ReactRoot = DOMRenderer.createContainer = createFiberRoot
// 参数element:{"$$typeof": Symbol(react.element),"key": null,"ref": null,"props": {},"_owner": null,"_store": {}}
render(element,container, // <div id="root"></div>callback,//undefined) {return legacyRenderSubtreeIntoContainer(null,element,container,false,callback,);}
function legacyRenderSubtreeIntoContainer(parentComponent, // nullchildren, //同上container, // <div id="root"></div>forceHydrate, // falsecallback // undefined) {container._reactRootContainer = legacyCreateRootFromDOMContainer(container,forceHydrate)root.render(children, callback)return DOMRenderer.getPublicRootInstance(root._internalRoot)}
function legacyCreateRootFromDOMContainer(container, forceHydrate) {return new ReactRoot(container, isConcurrent, shouldHydrate)}
function ReactRoot(container, isConcurrent, hydrate) {const root = DOMRenderer.createContainer(container, isConcurrent, hydrate)this._internalRoot = root}
export function createContainer(containerInfo, isConcurrent, hydrate) {return createFiberRoot(containerInfo, isConcurrent, hydrate)}
生成FiberRoot
和RootFiber
,它们互相引用
FiberRoot.current = RootFiber
RootFiber.stateNode = FiberRoot
export function createFiberRoot(containerInfo, isConcurrent, hydrate) {const uninitializedFiber = createHostRootFiber(isConcurrent)let rootif (enableSchedulerTracing) {root = {current: uninitializedFiber,containerInfo: containerInfo,pendingChildren: null,earliestPendingTime: NoWork,latestPendingTime: NoWork,earliestSuspendedTime: NoWork,latestSuspendedTime: NoWork,latestPingedTime: NoWork,didError: false,pendingCommitExpirationTime: NoWork,finishedWork: null,timeoutHandle: noTimeout,context: null,pendingContext: null,hydrate,nextExpirationTimeToWorkOn: NoWork,expirationTime: NoWork,firstBatch: null,nextScheduledRoot: null,interactionThreadID: unstable_getThreadID(),memoizedInteractions: new Set(),pendingInteractionMap: new Map(),}} else {root = {current: uninitializedFiber,containerInfo: containerInfo,pendingChildren: null,earliestPendingTime: NoWork,latestPendingTime: NoWork,earliestSuspendedTime: NoWork,latestSuspendedTime: NoWork,latestPingedTime: NoWork,didError: false,pendingCommitExpirationTime: NoWork,finishedWork: null,timeoutHandle: noTimeout,context: null,pendingContext: null,hydrate,nextExpirationTimeToWorkOn: NoWork,expirationTime: NoWork,firstBatch: null,nextScheduledRoot: null,}}uninitializedFiber.stateNode = rootreturn root}
export function createHostRootFiber(isConcurrent) {let mode = isConcurrent ? ConcurrentMode | StrictMode : NoContextif (enableProfilerTimer && isDevToolsPresent) {mode |= ProfileMode}return createFiber(HostRoot, null, null, mode)}
const createFiber = function (tag, pendingProps, key, mode) {return new FiberNode(tag, pendingProps, key, mode)}
function FiberNode(tag, pendingProps, key, mode) {this.tag = tagthis.key = keythis.elementType = nullthis.type = nullthis.stateNode = nullthis.return = nullthis.child = nullthis.sibling = nullthis.index = 0this.ref = nullthis.pendingProps = pendingPropsthis.memoizedProps = nullthis.updateQueue = nullthis.memoizedState = nullthis.firstContextDependency = nullthis.mode = modethis.effectTag = NoEffectthis.nextEffect = nullthis.firstEffect = nullthis.lastEffect = nullthis.expirationTime = NoWorkthis.childExpirationTime = NoWorkthis.alternate = nullif (enableProfilerTimer) {this.actualDuration = 0this.actualStartTime = -1this.selfBaseDuration = 0this.treeBaseDuration = 0}}
export type Fiber = {tag: WorkTag,key: null | string,elementType: any,type: any,stateNode: any,return: Fiber | null,child: Fiber | null,sibling: Fiber | null,index: number,ref: null | (((handle: mixed) => void) & {_stringRef: ?string}) | RefObject,pendingProps: any,memoizedProps: any,updateQueue: UpdateQueue<any> | null,memoizedState: any,firstContextDependency: ContextDependency<mixed> | null,mode: TypeOfMode,effectTag: SideEffectTag,nextEffect: Fiber | null,firstEffect: Fiber | null,lastEffect: Fiber | null,expirationTime: ExpirationTime,childExpirationTime: ExpirationTime,alternate: Fiber | null,actualDuration?: number,actualStartTime?: number,selfBaseDuration?: number,treeBaseDuration?: number,_debugID?: number,_debugSource?: Source | null,_debugOwner?: Fiber | null,_debugIsCurrentlyTiming?: boolean,|};
function unbatchedUpdates(fn) {if (isBatchingUpdates && !isUnbatchingUpdates) {isUnbatchingUpdates = truetry {return fn(a) //流程2:执行anonymous} finally {isUnbatchingUpdates = false}}return fn(a)}
ReactRoot.prototype.render = function (//流程3:执行React.renderchildren, //同上callback // undefined) {const root = this._internalRootconst work = new ReactWork()DOMRenderer.updateContainer(children, root, null, work._onCommit)return work}
// 参数container: {containerInfo: div#root,context: null,`current`: FiberNode {tag: 3, key: null, elementType: null, type: null, stateNode: {…}, …},didError: false,earliestPendingTime: 0,earliestSuspendedTime: 0,expirationTime: 0,finishedWork: null,firstBatch: null,hydrate: false,interactionThreadID: 1,latestPendingTime: 0,latestPingedTime: 0,latestSuspendedTime: 0,memoizedInteractions: Set(0) {},nextExpirationTimeToWorkOn: 0,nextScheduledRoot: null,pendingChildren: null,pendingCommitExpirationTime: 0,pendingContext: null,pendingInteractionMap: Map(0) {},timeoutHandle: -1}
export function updateContainer(element, //同上container,parentComponent, //nullcallback) {const current = container.currentconst currentTime = requestCurrentTime()const expirationTime = computeExpirationForFiber(currentTime, current)return updateContainerAtExpirationTime(element,container,parentComponent,expirationTime,callback)}
export function updateContainerAtExpirationTime(element, // 同上container, // 同上parentComponent, //nullexpirationTime, //1callback) {const current = container.currentconst context = getContextForSubtree(parentComponent)if (container.context === null) {container.context = context} else {container.pendingContext = context}return scheduleRootUpdate(current, element, expirationTime, callback)}
current: {actualDuration: 0actualStartTime: -1alternate: nullchild: nullchildExpirationTime: 0effectTag: 0elementType: nullexpirationTime: 0firstContextDependency: nullfirstEffect: nullindex: 0key: nulllastEffect: nullmemoizedProps: nullmemoizedState: nullmode: 0nextEffect: nullpendingProps: nullref: nullreturn: nullselfBaseDuration: 0sibling: nullstateNode: {current: FiberNode, containerInfo: div#root, pendingChildren: null, earliestPendingTime: 0, latestPendingTime: 0, …}tag: 3treeBaseDuration: 0type: nullupdateQueue: null_debugID: 1_debugIsCurrentlyTiming: false_debugOwner: null_debugSource: null}
function scheduleRootUpdate(current,element, // 同上expirationTime, //1callback) {const update = createUpdate(expirationTime) // 1.创建更新update.payload = { element }callback = callback === undefined ? null : callbackif (callback !== null) {update.callback = callback}enqueueUpdate(current, update) // 2.将更新放进更新队列,将更新队列绑定到fiber上,也就是currentscheduleWork(current, expirationTime) // 3.调用scheduleWork进行更新调度return expirationTime}
requestWork
function scheduleWork(fiber, // 同上expirationTime // 1) {const root = scheduleWorkToRoot(fiber, expirationTime) //1.获取当前更新的root节点markPendingPriorityLevel(root, expirationTime) //2.将该root加入更新链表if (!isWorking || isCommitting || nextRoot !== root) {const rootExpirationTime = root.expirationTimerequestWork(root, rootExpirationTime)}}
root:{containerInfo: div#root,`context`: {},current: FiberNode {tag: 3, key: null, elementType: null, type: null, stateNode: {…}, …},didError: false,`earliestPendingTime`: 1,earliestSuspendedTime: 0,`expirationTime`: 1,finishedWork: nullfirstBatch: null,hydrate: false,interactionThreadID: 1,`latestPendingTime`: 1,latestPingedTime: 0,latestSuspendedTime: 0,memoizedInteractions: Set(0) {},`nextExpirationTimeToWorkOn`: 1,nextScheduledRoot: null,pendingChildren: null,pendingCommitExpirationTime: 0,pendingContext: null,pendingInteractionMap: Map(0) {},timeoutHandle: -1}
function requestWork(root,expirationTime // 1) {addRootToSchedule(root, expirationTime)performSyncWork() //同步更新}
function performSyncWork() {performWork(Sync, null)}
function performWork(minExpirationTime, //1dl) {deadline = dlfindHighestPriorityRoot()while (nextFlushedRoot !== null &&nextFlushedExpirationTime !== NoWork &&(minExpirationTime === NoWork ||minExpirationTime >= nextFlushedExpirationTime)) {performWorkOnRoot(nextFlushedRoot, nextFlushedExpirationTime, true)findHighestPriorityRoot()}deadline = nulldeadlineDidExpire = falsefinishRendering()}
root:{containerInfo: div#rootcontext: {}current: FiberNode {tag: 3, key: null, elementType: null, type: null, stateNode: {…}, …}didError: falseearliestPendingTime: 1earliestSuspendedTime: 0expirationTime: 1finishedWork: nullfirstBatch: nullhydrate: falseinteractionThreadID: 1latestPendingTime: 1latestPingedTime: 0latestSuspendedTime: 0memoizedInteractions: Set(0) {}nextExpirationTimeToWorkOn: 1`nextScheduledRoot`: {current: FiberNode, containerInfo: div#root, pendingChildren: null, earliestPendingTime: 1, latestPendingTime: 1, …}pendingChildren: nullpendingCommitExpirationTime: 0pendingContext: nullpendingInteractionMap: Map(0) {}timeoutHandle: -1}
function performWorkOnRoot(root,expirationTime, // 1isExpired //true) {isRendering = truelet finishedWork = root.finishedWorkroot.finishedWork = nullconst timeoutHandle = root.timeoutHandleconst isYieldy = false //可中断renderRoot(root, isYieldy, isExpired)finishedWork = root.finishedWorkif (finishedWork !== null) {completeRoot(root, finishedWork, expirationTime)}isRendering = false}
function renderRoot(root, //同上isYieldy, //falseisExpired //true) {isWorking = trueReactCurrentOwner.currentDispatcher = Dispatcherconst expirationTime = root.nextExpirationTimeToWorkOnresetStack()nextRoot = rootnextRenderExpirationTime = expirationTimenextUnitOfWork = createWorkInProgress(nextRoot.current,null,nextRenderExpirationTime)root.pendingCommitExpirationTime = NoWorkif (enableSchedulerTracing) {const interactions = new Set()root.pendingInteractionMap.forEach((scheduledInteractions, scheduledExpirationTime) => {if (scheduledExpirationTime <= expirationTime) {scheduledInteractions.forEach((interaction) =>interactions.add(interaction))}})root.memoizedInteractions = interactions}let prevInteractionsif (enableSchedulerTracing) {prevInteractions = __interactionsRef.current__interactionsRef.current = root.memoizedInteractions}let didFatal = falsestartWorkLoopTimer(nextUnitOfWork)do {try {workLoop(isYieldy) //循环所有节点} catch (thrownValue) {}break} while (true)if (enableSchedulerTracing) {__interactionsRef.current = prevInteractions}isWorking = falseReactCurrentOwner.currentDispatcher = nullresetContextDependences()const didCompleteRoot = truestopWorkLoopTimer(interruptedBy, didCompleteRoot)const rootWorkInProgress = root.current.alternatenextRoot = nullinterruptedBy = nullonComplete(root, rootWorkInProgress, expirationTime)}
function workLoop(isYieldy //false,可中断) {if (!isYieldy) {while (nextUnitOfWork !== null) {nextUnitOfWork = performUnitOfWork(nextUnitOfWork)}}}
workInProgress:{actualDuration: 0actualStartTime: -1alternate: FiberNode {tag: 3, key: null, elementType: null, type: null, stateNode: {…}, …}child: nullchildExpirationTime: 0effectTag: 0elementType: nullexpirationTime: 1firstContextDependency: nullfirstEffect: nullindex: 0key: nulllastEffect: nullmemoizedProps: nullmemoizedState: nullmode: 0nextEffect: nullpendingProps: nullref: nullreturn: nullselfBaseDuration: 0sibling: nullstateNode: {current: FiberNode, containerInfo: div#root, pendingChildren: null, earliestPendingTime: 1, latestPendingTime: 1, …}tag: 3treeBaseDuration: 0type: nullupdateQueue: {baseState: null, firstUpdate: {…}, lastUpdate: {…}, firstCapturedUpdate: null, lastCapturedUpdate: null, …}_debugID: 1_debugIsCurrentlyTiming: false_debugOwner: null_debugSource: null}
function performUnitOfWork(workInProgress) {const current = workInProgress.alternatestartWorkTimer(workInProgress)let nextif (enableProfilerTimer) {next = beginWork(current, workInProgress, nextRenderExpirationTime)workInProgress.memoizedProps = workInProgress.pendingPropsif (workInProgress.mode & ProfileMode) {stopProfilerTimerIfRunningAndRecordDelta(workInProgress, true)}}if (next === null) {next = completeUnitOfWork(workInProgress)}ReactCurrentOwner.current = nullreturn next}
current:{actualDuration: 0actualStartTime: -1alternate: FiberNode {tag: 3, key: null, elementType: null, type: null, stateNode: {…}, …}child: nullchildExpirationTime: 0effectTag: 0elementType: nullexpirationTime: 1firstContextDependency: nullfirstEffect: nullindex: 0key: nulllastEffect: nullmemoizedProps: nullmemoizedState: nullmode: 0nextEffect: nullpendingProps: nullref: nullreturn: nullselfBaseDuration: 0sibling: nullstateNode: {current: FiberNode, containerInfo: div#root, pendingChildren: null, earliestPendingTime: 1, latestPendingTime: 1, …}tag: 3treeBaseDuration: 0type: nullupdateQueue: {baseState: null, firstUpdate: {…}, lastUpdate: {…}, firstCapturedUpdate: null, lastCapturedUpdate: null, …}_debugID: 1_debugIsCurrentlyTiming: false_debugOwner: null_debugSource: null}
function beginWork(current,workInProgress, //同上renderExpirationTime //1) {const updateExpirationTime = workInProgress.expirationTimeworkInProgress.expirationTime = NoWorkswitch (workInProgress.tag) {case HostRoot:return updateHostRoot(current, workInProgress, renderExpirationTime)}}
workInProgress:{actualDuration: 0actualStartTime: -1alternate: FiberNode {tag: 3, key: null, elementType: null, type: null, stateNode: {…}, …}child: nullchildExpirationTime: 0effectTag: 0elementType: null`expirationTime`: 0firstContextDependency: nullfirstEffect: nullindex: 0key: nulllastEffect: nullmemoizedProps: nullmemoizedState: nullmode: 0nextEffect: nullpendingProps: nullref: nullreturn: nullselfBaseDuration: 0sibling: nullstateNode: {current: FiberNode, containerInfo: div#root, pendingChildren: null, earliestPendingTime: 1, latestPendingTime: 1, …}tag: 3treeBaseDuration: 0type: nullupdateQueue: {baseState: null, firstUpdate: {…}, lastUpdate: {…}, firstCapturedUpdate: null, lastCapturedUpdate: null, …}_debugID: 1_debugIsCurrentlyTiming: false_debugOwner: null_debugSource: null}
function updateHostRoot(current, //同上workInProgress,renderExpirationTime) {pushHostRootContext(workInProgress)var updateQueue = workInProgress.updateQueuevar nextProps = workInProgress.pendingPropsvar prevState = workInProgress.memoizedStatevar prevChildren = prevState !== null ? prevState.element : nullprocessUpdateQueue(workInProgress,updateQueue,nextProps,null,renderExpirationTime)var nextState = workInProgress.memoizedStatevar nextChildren = nextState.elementvar root = workInProgress.stateNodereconcileChildren(current, workInProgress, nextChildren, renderExpirationTime)resetHydrationState()return workInProgress.child}
workInProgress:{`workInProgress` = FiberNode {tag: 2, key: null, stateNode: null, elementType: ƒ, type: ƒ, …}`Local``current$$1`: undefined`next`: undefined`this`: undefined`workInProgress`: FiberNode {tag: 2, key: null, stateNode: null, elementType: ƒ, type: ƒ, …}`Closure``Closure` (./node_modules/react-dom/cjs/react-dom.development.js)`Window``Global``FiberNode`actualDuration: 0actualStartTime: -1`alternate`: nullchild: nullchildExpirationTime: 0`effectTag`: 2`elementType`: ƒ App()`expirationTime`: 1firstContextDependency: nullfirstEffect: nullindex: 0key: nulllastEffect: nullmemoizedProps: nullmemoizedState: nullmode: 0nextEffect: null`pendingProps`: {}ref: null`return`: FiberNode {tag: 3, key: null, elementType: null, type: null, stateNode: {…}, …}selfBaseDuration: 0`sibling`: null`stateNode`: null`tag`: 2treeBaseDuration: 0`type`: ƒ App()`updateQueue`: null`_debugID`: 4`_debugIsCurrentlyTiming`: false_debugOwner: null_debugSource: null}
function performUnitOfWork(workInProgress) {const current = workInProgress.alternatestartWorkTimer(workInProgress)let nextif (enableProfilerTimer) {next = beginWork(current, workInProgress, nextRenderExpirationTime)workInProgress.memoizedProps = workInProgress.pendingPropsif (workInProgress.mode & ProfileMode) {stopProfilerTimerIfRunningAndRecordDelta(workInProgress, true)}}if (next === null) {next = completeUnitOfWork(workInProgress)}ReactCurrentOwner.current = nullreturn next}
function beginWork(current,//nullworkInProgress,//同上renderExpirationTime,//1){const updateExpirationTime = workInProgress.expirationTime;workInProgress.expirationTime = NoWork;switch (workInProgress.tag) {case IndeterminateComponent:{var elementType = workInProgress.elementType;return mountIndeterminateComponent(current, workInProgress, elementType, renderExpirationTime);}}
提交阶段
workInProgress:{actualDuration: 0actualStartTime: -1alternate: nullchild: nullchildExpirationTime: 0effectTag: 3elementType: ƒ App()`expirationTime`: 0firstContextDependency: nullfirstEffect: nullindex: 0key: nulllastEffect: nullmemoizedProps: nullmemoizedState: nullmode: 0nextEffect: nullpendingProps: {}ref: nullreturn: FiberNode {tag: 3, key: null, elementType: null, type: null, stateNode: {…}, …}selfBaseDuration: 0sibling: nullstateNode: nulltag: 2treeBaseDuration: 0type: ƒ App()updateQueue: null_debugID: 4_debugIsCurrentlyTiming: true_debugOwner: null_debugSource: null}
function mountIndeterminateComponent(_current, //nullworkInProgress,Component, //ApprenderExpirationTime //1) {const props = workInProgress.pendingPropsconst unmaskedContext = getUnmaskedContext(workInProgress, Component, false)const context = getMaskedContext(workInProgress, unmaskedContext)prepareToReadContext(workInProgress, renderExpirationTime)let valuevalue = Component(props, context)workInProgress.effectTag |= PerformedWorkworkInProgress.tag = FunctionComponentreconcileChildren(null, workInProgress, value, renderExpirationTime)return workInProgress.child}
function App() {return <div>hello</div>}