This is the 8th day of my participation in the August More Text Challenge. For details, see:August is more challenging
This section is based on ReACTV17.0.2 and builds entirely on the basics introduced earlier in Fiber Tree construction (Foundation preparation), which summarizes two cases of fiber tree construction:
- First created: in
React
When the app is first launched, the interface has not yet been rendered, and the comparison process does not begin. It is equivalent to constructing a completely new tree. - Comparison update:
React
After the application starts, the interface has been rendered. If the update occurs again, createThe new fiber
Before you need to sumThe old fiber
The resulting fiber tree may be completely new or partially updated.
In this section, we only discuss the initial creation of the fiber tree. In order to control the length of the process (this section will go straight to the core source code and not cover the basics, see Fiber Tree construction (foundation preparation)) and highlight the fiber tree construction process, we will analyze it later in Legacy mode (since we only discuss the principle of fiber tree construction). Concurrent mode is no different from Legacy).
The example code for this section is as follows (codesandbox address):
class App extends React.Component {
componentDidMount() {
console.log(`App Mount`);
console.log('App component corresponding fiber node:'.this._reactInternals);
}
render() {
return (
<div className="app">
<header>header</header>
<Content />
</div>); }}class Content extends React.Component {
componentDidMount() {
console.log(`Content Mount`);
console.log(The fiber node corresponding to the 'Content component:'.this._reactInternals);
}
render() {
return (
<React.Fragment>
<p>1</p>
<p>2</p>
</React.Fragment>); }}export default App;
Copy the code
Startup phase
The differences between the three startup modes are analyzed during the React application startup process. Before entering the React-Reconciler package (before calling updateContainer), the memory state diagram is as follows:
Based on this structure, you can print out the fiber tree corresponding to the current page in the console (to see its structure):
document.getElementById('root')._reactRootContainer._internalRoot.current;
Copy the code
Then go to the React-Reconciler package and call the updateContainer function:
/ /... Part of the code is omitted
export function updateContainer(element: ReactNodeList, container: OpaqueRoot, parentComponent: ? React$Component<any, any>, callback: ?Function.) :Lane {
// Get the current timestamp
const current = container.current;
const eventTime = requestEventTime();
// 1. Create a priority variable (lane model)
const lane = requestUpdateLane(current);
/ / 2. According to priority lanes, create the update object, and add fiber. UpdateQueue. Pending queue
const update = createUpdate(eventTime, lane);
update.payload = { element };
callback = callback === undefined ? null : callback;
if(callback ! = =null) {
update.callback = callback;
}
enqueueUpdate(current, update);
// 3. Enter the 'input' section of the reconcier operation process
scheduleUpdateOnFiber(current, lane, eventTime);
return lane;
}
Copy the code
Due to theupdate
Object, the memory structure is as follows:
Note: the original ReactElement object < App / > be mounted to HostRootFiber. UpdateQueue. Shared. Pending. Content. The element, later in the process of the fiber structure tree will change again.
Construction phase
In order to highlight the construction process and eliminate interference, FiberRoot and HostRootFiber in the memory state diagram are proposed separately (added later on the basis of them):
In scheduleUpdateOnFiber:
/ /... Omit part of the code
export function scheduleUpdateOnFiber(fiber: Fiber, lane: Lane, eventTime: number,) {
// Mark the priority
const root = markUpdateLaneFromFiberToRoot(fiber, lane);
if (lane === SyncLane) {
if( (executionContext & LegacyUnbatchedContext) ! == NoContext && (executionContext & (RenderContext | CommitContext)) === NoContext ) {// For the first render, do the 'fiber construct' directly
performSyncWorkOnRoot(root);
}
// ...}}Copy the code
As you can see, rendering, for the first time in Legacy mode and markUpdateLaneFromFiberToRoot and performSyncWorkOnRoot has two functions.
MarkUpdateLaneFromFiberToRoot fiber, lane () function in the fiber structure tree (in contrast to update) will play a role, because in the created for the first time and not with the current page of the fiber tree, so the core code does not perform, Finally, the FiberRoot object is returned directly.
PerformSyncWorkOnRoot looks like a lot of source code, the first time the real use of the two functions:
function performSyncWorkOnRoot(root) {
let lanes;
let exitStatus;
if (
root === workInProgressRoot &&
includesSomeLane(root.expiredLanes, workInProgressRootRenderLanes)
) {
// When first constructed (because root=fiberRoot, workInProgressRoot=null), it will not enter
} else {
}}}}}}}}}}}}}}}}}}}}}
lanes = getNextLanes(root, NoLanes);
// 2. Update from the root node to the bottom
exitStatus = renderRootSync(root, lanes);
}
// Mount the latest fiber tree to the root.finishedWork node
const finishedWork: Fiber = (root.current.alternate: any);
root.finishedWork = finishedWork;
root.finishedLanes = lanes;
// Enter the COMMIT phase
commitRoot(root);
/ /... The rest is not covered in this section
}
Copy the code
GetNextLanes returns the priority of the render (see the priority section of Fiber Tree Construction (Basic Preparation)).
renderRootSync
function renderRootSync(root: FiberRoot, lanes: Lanes) {
const prevExecutionContext = executionContext;
executionContext |= RenderContext;
// If fiberRoot changes, or update.lane changes, the stack frame is refreshed and the last render progress is discarded
if(workInProgressRoot ! == root || workInProgressRootRenderLanes ! == lanes) {// Refresh the stack frame, both in Legacy mode
prepareFreshStack(root, lanes);
}
do {
try {
workLoopSync();
break;
} catch(thrownValue) { handleError(root, thrownValue); }}while (true);
executionContext = prevExecutionContext;
// Reset the global variable to indicate the end of render
workInProgressRoot = null;
workInProgressRootRenderLanes = NoLanes;
return workInProgressRootExitStatus;
}
Copy the code
In renderRootSync, the stack frame prepareFreshStack is refreshed before performing the Fiber tree construction (workLoopSync) (see Stack frame management in Fiber Tree Construction (Basic Preparation)). Here hostRootFiber. alternate is created and global variables workInProgress and workInProgressRoot are reset.
Loop structure
The logic goes to workLoopSync, which is compared to workLoopConcurrent, although this section discusses it in Legacy mode
function workLoopSync() {
while(workInProgress ! = =null) { performUnitOfWork(workInProgress); }}function workLoopConcurrent() {
// Perform work until Scheduler asks us to yield
while(workInProgress ! = =null&&! shouldYield()) { performUnitOfWork(workInProgress); }}Copy the code
You can see that workLoopConcurrent has an additional pause mechanism compared to Sync, which implements time slicing and interruptible rendering (see React scheduling principles).
Combine the performUnitOfWork function (source address)
/ /... Omit some irrelevant code
function performUnitOfWork(unitOfWork: Fiber) :void {
// unitOfWork is the workInProgress passed in
const current = unitOfWork.alternate;
let next;
next = beginWork(current, unitOfWork, subtreeRenderLanes);
unitOfWork.memoizedProps = unitOfWork.pendingProps;
if (next === null) {
// If no new node is derived, the completeWork phase is entered, passing in the current unitOfWork
completeUnitOfWork(unitOfWork);
} else{ workInProgress = next; }}Copy the code
It is obvious that the whole Fiber tree is a depth-first traversal (refer to the React depth-first traversal), and there are two important variables workInProgress and current(refer to the double buffering technique introduced in the Fiber tree construction (basic preparation) above):
workInProgress
andcurrent
Are all PointersworkInProgress
Points to what is currently under constructionfiber
nodecurrent = workInProgress.alternate
(i.e.fiber.alternate
) to which the page is currently in usefiber
When the page is first constructed, it has not yet been renderedcurrent = null
.
In depth-first traversal, each fiber node goes through two stages:
- Exploring stage
beginWork
- Back stage
completeWork
These two stages together complete the creation of each fiber node, and all fiber nodes constitute the Fiber tree.
The search stage beginWork
BeginWork (Current, unitOfWork, subtreeRenderLanes)(source address) for all Fiber types, and each case processes one Fiber type. UpdateHostRoot, updateClassComponent, etc.)
- According to the
ReactElement
Object to create allfiber
Node, finally constructedFiber tree structure
(setreturn
andsibling
Pointer) - Set up the
fiber.flags
(Binary form variable, used to markfiber
The node’sAdd, delete, change
Status, waitingThe completeWork phase is processed
) - Set up the
fiber.stateNode
Local states (e.gThe Class type
Nodes:fiber.stateNode=new Class()
)
function beginWork(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
) :Fiber | null {
const updateLanes = workInProgress.lanes;
if(current ! = =null) {
// Update logic, render will not enter the first time
} else {
didReceiveUpdate = false;
}
// 1. Set workInProgress priority to NoLanes(highest priority)
workInProgress.lanes = NoLanes;
// 2. Based on the type of the workInProgress node, use different methods to derive child nodes
switch (
workInProgress.tag // Keep only the case used in this example
) {
case ClassComponent: {
const Component = workInProgress.type;
const unresolvedProps = workInProgress.pendingProps;
const resolvedProps =
workInProgress.elementType === Component
? unresolvedProps
: resolveDefaultProps(Component, unresolvedProps);
return updateClassComponent(
current,
workInProgress,
Component,
resolvedProps,
renderLanes,
);
}
case HostRoot:
return updateHostRoot(current, workInProgress, renderLanes);
case HostComponent:
return updateHostComponent(current, workInProgress, renderLanes);
case HostText:
return updateHostText(current, workInProgress);
case Fragment:
returnupdateFragment(current, workInProgress, renderLanes); }}Copy the code
UpdateXXX (such as: updateHostRoot, updateClassComponent, etc.) has many cases, but the main logic can be summarized as 3 steps:
- According to the
fiber.pendingProps, fiber.updateQueue
Etc.The input data
State, calculationfiber.memoizedState
As aOutput state
- Access to the lower
ReactElement
object- The class type
fiber
node- build
React.Component
The instance - Mount the new instance to
fiber.stateNode
on - perform
render
The previous lifecycle function - perform
render
Method to get the subordinatereactElement
- Set the parameters based on the actual situation
fiber.flags
- build
- The function type
fiber
node- Execute function to get the child
reactElement
- Set the parameters based on the actual situation
fiber.flags
- Execute function to get the child
- The HostComponent type (e.g.
div, span, button
, etc.)fiber
nodependingProps.children
As a juniorreactElement
- If the child node is a text node, set the child node to null. Prepare to enter
completeUnitOfWork
phase - Set the parameters based on the actual situation
fiber.flags
- Other types…
- The class type
- According to the
ReactElement
Object, callreconcilerChildren
generateFiber
Child nodes (only generatedSecondary child node
)- Set the parameters based on the actual situation
fiber.flags
- Set the parameters based on the actual situation
Different updateXXX functions handle different types of fiber nodes, with the overall purpose of generating child nodes down. In the process to mount need some persistent data in fiber node (such as fiber. StateNode, fiber memoizedState, etc.); Set the special operations of the fiber node to fiber.flags(e.g., node ref,class lifecycle,function hook, node delete, etc.).
UpdateHostRoot and updateHostComponent code are listed here. Analysis of other common cases (such as class and function types) is discussed in the status Components section.
The root node of fiber tree is HostRootFiber node, so the first time to enter beginWork will call updateHostRoot(Current, workInProgress, renderLanes).
// Omit code not relevant to this section
function updateHostRoot(current, workInProgress, renderLanes) {
/ / 1. State calculation, update integrated into workInProgress. MemoizedState
const updateQueue = workInProgress.updateQueue;
const nextProps = workInProgress.pendingProps;
const prevState = workInProgress.memoizedState;
constprevChildren = prevState ! = =null ? prevState.element : null;
cloneUpdateQueue(current, workInProgress);
/ / traverse updateQueue. Shared. Pending, extract enough priority update object, calculate the final state workInProgress. MemoizedState
processUpdateQueue(workInProgress, nextProps, null, renderLanes);
const nextState = workInProgress.memoizedState;
// 2. Obtain the subordinate 'ReactElement' object
const nextChildren = nextState.element;
const root: FiberRoot = workInProgress.stateNode;
if (root.hydrate && enterHydrationState(workInProgress)) {
/ /... Server rendering related, omitted here
} else {
// 3. Use 'reconcilerChildren' to generate 'Fiber' child nodes based on the 'ReactElement' object from the ground up (only 'sub-nodes' are generated)
reconcileChildren(current, workInProgress, nextChildren, renderLanes);
}
return workInProgress.child;
}
Copy the code
Normal DOM tag types (such as div,span,p) will enter updateHostComponent:
/ /... Omit some irrelevant code
function updateHostComponent(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
) {
// there is no memoizedState for HostComponent. There is no memoizedState for HostComponent
const type = workInProgress.type;
const nextProps = workInProgress.pendingProps;
constprevProps = current ! = =null ? current.memoizedProps : null;
// 2. Obtain the subordinate 'ReactElement' object
let nextChildren = nextProps.children;
const isDirectTextChild = shouldSetTextContent(type, nextProps);
if (isDirectTextChild) {
// If the child node has only one text node, you don't need to create a fiber of type HostText
nextChildren = null;
} else if(prevProps ! = =null && shouldSetTextContent(type, prevProps)) {
// Special operations need to set fiber.flags
workInProgress.flags |= ContentReset;
}
// Special operations need to set fiber.flags
markRef(current, workInProgress);
// 3. Use 'reconcilerChildren' to generate 'Fiber' child nodes based on the 'ReactElement' object from the ground up (only 'sub-nodes' are generated)
reconcileChildren(current, workInProgress, nextChildren, renderLanes);
return workInProgress.child;
}
Copy the code
Backtrack phase completeWork
CompleteUnitOfWork (unitOfWork)(source address), process the beginWork phase has created the fiber node, the core logic:
- call
completeWork
- to
fiber
The node (Tag =HostComponent, HostText) creates a DOM instance and sets itfiber.stateNode
Local states (e.gtag=HostComponent, HostText
Node: fiber.statenode points to the DOM instance). - Set the properties for the DOM node, bind the event (here describes this step first, the detailed event processing process, in
Principle of synthetic events
). - Set up the
fiber.flags
tag
- to
- The current
fiber
Object’s side effect queue (firstEffect
andlastEffect
) is added to the parent node’s side effect queue to update the parent node’sfirstEffect
andlastEffect
Pointer. - identify
beginWork
stage-setfiber.flags
To judge the currentfiber
Whether there are side effects (add, delete, change), if so, need to be currentfiber
Added to the parent nodeeffects
Queue, waitcommit
Stage processing.
function completeUnitOfWork(unitOfWork: Fiber) :void {
let completedWork = unitOfWork;
// The outer loop controls and moves the pointer (' workInProgress ', 'completedWork', etc.)
do {
const current = completedWork.alternate;
const returnFiber = completedWork.return;
if ((completedWork.flags & Incomplete) === NoFlags) {
let next;
// 1. When processing the Fiber node, the renderer is called (call the React-DOM package, associate the Fiber node with the DOM object, bind the event, etc.).
next = completeWork(current, completedWork, subtreeRenderLanes); // Process a single node
if(next ! = =null) {
// If other child nodes are derived, then go back to the 'beginWork' phase for processing
workInProgress = next;
return;
}
// Reset the priority of the child node
resetChildLanes(completedWork);
if( returnFiber ! = =null &&
(returnFiber.flags & Incomplete) === NoFlags
) {
// 2. Collect the side effects of the current Fiber node and its subtrees
// 2.1 Add the child node's side effect queue to the parent node
if (returnFiber.firstEffect === null) {
returnFiber.firstEffect = completedWork.firstEffect;
}
if(completedWork.lastEffect ! = =null) {
if(returnFiber.lastEffect ! = =null) {
returnFiber.lastEffect.nextEffect = completedWork.firstEffect;
}
returnFiber.lastEffect = completedWork.lastEffect;
}
// 2.2 If the current fiber node has side effects, add it to the child node's side effects queue.
const flags = completedWork.flags;
if (flags > PerformedWork) {
// PerformedWork is read by React DevTools, so skip it
if(returnFiber.lastEffect ! = =null) {
returnFiber.lastEffect.nextEffect = completedWork;
} else{ returnFiber.firstEffect = completedWork; } returnFiber.lastEffect = completedWork; }}}else {
// Exception handling is not covered in this section
}
const siblingFiber = completedWork.sibling;
if(siblingFiber ! = =null) {
// If there are siblings, return and enter the 'beginWork' phase again
workInProgress = siblingFiber;
return;
}
// Move the pointer to the next node
completedWork = returnFiber;
workInProgress = completedWork;
} while(completedWork ! = =null);
/ / has been back to the root node, set workInProgressRootExitStatus = RootCompleted
if(workInProgressRootExitStatus === RootIncomplete) { workInProgressRootExitStatus = RootCompleted; }}Copy the code
Next, analyze the fiber handler function completeWork
function completeWork(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
) :Fiber | null {
const newProps = workInProgress.pendingProps;
switch (workInProgress.tag) {
case ClassComponent: {
// The Class type is not processed
return null;
}
case HostRoot: {
const fiberRoot = (workInProgress.stateNode: FiberRoot);
if (fiberRoot.pendingContext) {
fiberRoot.context = fiberRoot.pendingContext;
fiberRoot.pendingContext = null;
}
if (current === null || current.child === null) {
// Set the fiber. Flags flag
workInProgress.flags |= Snapshot;
}
return null;
}
case HostComponent: {
popHostContext(workInProgress);
const rootContainerInstance = getRootHostContainer();
const type = workInProgress.type;
if(current ! = =null&& workInProgress.stateNode ! =null) {
// Update logic, render will not enter the first time
} else {
const currentHostContext = getHostContext();
// 1. Create DOM objects
const instance = createInstance(
type,
newProps,
rootContainerInstance,
currentHostContext,
workInProgress,
);
// 2. Append the DOM object in the subtree to the DOM object in this node
appendAllChildren(instance, workInProgress, false.false);
// Set the stateNode property to point to the DOM object
workInProgress.stateNode = instance;
if (
// 3. Set the properties of the DOM object, bind events, etc
finalizeInitialChildren(
instance,
type,
newProps,
rootContainerInstance,
currentHostContext,
)
) {
// Set fibre. flags (Update)
markUpdate(workInProgress);
}
if(workInProgress.ref ! = =null) {
// Set fibre.flags (Ref)
markRef(workInProgress);
}
return null; }}}Copy the code
It can be seen that fiber.flags will also be set when the conditions are met, so the setting of fiber.flags is not only in the beginWork phase.
Process diagram
For the sample code in this section, represent the entire fiber tree construction process:
Before construction:
PrepareFreshStack is called to refresh the stack frame before entering the loop, and this initialization state is maintained until entering the fiber tree loop:
PerformUnitOfWork First call (beginWork only):
- Perform before:
workInProgress
Pointer toHostRootFiber.alternate
Object, at which pointcurrent = workInProgress.alternate
Point to thefiberRoot.current
Non-empty (first construct, only at root node,current
Not empty). - Execution process: call
updateHostRoot
- in
reconcilerChildren
Stage, downward constructionSubnode fiber(<App/>)
, and set the child node (fiber(<App/>)
)fiber.flags |= Placement
- in
- After execution: Returns the lower-level node
fiber(<App/>)
, the mobileworkInProgress
The pointer points to the child nodefiber(<App/>)
PerformUnitOfWork second call (beginWork only):
- Perform before:
workInProgress
Pointer tofiber(<App/>)
Node, wherecurrent = null
- Execution process: call
updateClassComponent
- In this example, the class instance has a lifecycle function
componentDidMount
, so it will be setfiber(<App/>)
nodeworkInProgress.flags |= Update - Also for the sake of
React DevTools
Can identify the status of the component’s execution progress, will setworkInProgress.flags |= PerformedWork(in thecommit
The phase will rule that outflag
, listed here onlyworkInProgress.flags
The setup scenario is not discussedReact DevTools
) - Need to pay attention to
classInstance.render()
After this step, although the returnrender
MethodReactElement
Object, but thenreconcilerChildren
Only structureSecondary child node
- in
reconcilerChildren
Stage, downward constructionSecondary child node div
- In this example, the class instance has a lifecycle function
- After execution: Returns the lower-level node
fiber(div)
, the mobileworkInProgress
The pointer points to the child nodefiber(div)
PerformUnitOfWork the third call (beginWork only):
- Perform before:
workInProgress
Pointer tofiber(div)
Node, wherecurrent = null
- Execution process: call
updateHostComponent
- in
reconcilerChildren
Stage, downward constructionSecondary child node
(In this example,div
There are two secondary children)
- in
- After execution: Returns the lower-level node
fiber(header)
, the mobileworkInProgress
The pointer points to the child nodefiber(header)
PerformUnitOfWork fourth call (perform beginWork and completeUnitOfWork):
beginWork
Perform before:workInProgress
Pointer tofiber(header)
Node, wherecurrent = null
beginWork
Execution process: callupdateHostComponent
- In this example
header
The child node of is aDirect text nodeTo set upnextChildren = null(The source code comment explains that there is no need to open up memory to create a text node, while reducing traversal). - Due to the
nextChildren = null
, afterreconcilerChildren
After phase processing, the return value is alsonull
- In this example
beginWork
After execution: Because the lower-level node isnull
, so entercompleteUnitOfWork(unitOfWork)
Function, the parameters passed inunitOfWork
It’s essentiallyworkInProgress
(This point points tofiber(header)
Node)
completeUnitOfWork
Perform before:workInProgress
Pointer tofiber(header)
nodecompleteUnitOfWork
Implementation process: tofiber(header)
For the starting point, back up
The first cycle:
- perform
completeWork
function- create
fiber(header)
Node correspondingDOM
Instance andappend
The child nodes of theDOM
The instance - Set up the
DOM
Properties, binding events, etc. (in this case, nodefiber(header)
No event binding)
- create
- Move up side queue: due to this node
fiber(header)
No side effects (fiber.flags = 0
), so the side effect queue does not change substantially after execution (it is currently empty). - Backtracking upwards: since there are sibling nodes, the
workInProgress
The pointer points to the next siblingfiber(<Content/>)
To quitcompleteUnitOfWork
.
PerformUnitOfWork the fifth call (beginWork):
- Perform before:
workInProgress
Pointer tofiber(<Content/>)
Node. - Implementation process: This is a
class
Type, consistent with the second call logic. - After execution: Returns the lower-level node
fiber(p)
, the mobileworkInProgress
The pointer points to the child nodefiber(p)
PerformUnitOfWork 6th call (perform beginWork and completeUnitOfWork): same logic as the 4th call to create the fiber(header) node. BeginWork and completeUnitOfWork will be executed, and finally the DOM instance will be constructed, and the workInProgress pointer will be pointed to the next sibling node fiber(p).
PerformUnitOfWork the seventh call (perform beginWork and completeUnitOfWork):
beginWork
Execution procedure: Created in the last callfiber(p)
The logic of the nodes is consistentcompleteUnitOfWork
Implementation process: tofiber(p)
For the starting point, back up
The first cycle:
- perform
completeWork
Function: createfiber(p)
Node correspondingDOM
Instance andappend
Subtree nodeDOM
The instance - Move up side queue: due to this node
fiber(p)
There are no side effects, so the side effects queue does not change substantially after execution (it is currently empty). - Backtracking upwards: since there is no sibling node, the
workInProgress
The pointer points to the parentfiber(<Content/>)
The second cycle:
- perform
completeWork
Function: Nodes of type class are not processed - Moving up the side effect queue:
- This node
fiber(<Content/>)
theflags
Flag bit changed (completedWork.flags > PerformedWork
) to add this node to the parent node (fiber(div)
) after the side effects queue (firstEffect
andlastEffect
Property points to the head and tail of the side effect queue, respectively.
- This node
- Back up: put
workInProgress
The pointer points to the parentfiber(div)
The third cycle:
- perform
completeWork
Function: createfiber(div)
Node correspondingDOM
Instance andappend
Subtree nodeDOM
The instance - Moving up the side effect queue:
- This node
fiber(div)
The side effect queue is not empty, splicing it to the parent nodefiber<App/>
Side effects queue.
- This node
- Back up: put
workInProgress
The pointer points to the parentfiber(<App/>)
The fourth cycle:
- perform
completeWork
Function: Nodes of type class are not processed - Moving up the side effect queue:
- This node
fiber(<App/>)
The side effect queue is not empty, splicing it to the parent nodefiber(HostRootFiber)
Side effects on the queue. - This node
fiber(<App/>)
theflags
Flag bit changed (completedWork.flags > PerformedWork
) to add this node to the parent nodefiber(HostRootFiber)
The side effects queue after. - The order of the last queue is
The child node is first, and the local node is second
- This node
- Back up: put
workInProgress
The pointer points to the parentfiber(HostRootFiber)
The fifth cycle:
- perform
completeWork
Function: forHostRoot
Type of node, set when first constructedworkInProgress.flags |= Snapshot - Backtracking up: Since the parent node is empty, there is no need to enter the logic to process the side effect queue. The last set
workInProgress=null
And out ofcompleteUnitOfWork
At this point, the entire fiber tree construction loop has been executed, with a complete fiber tree and side effect queues mounted on the root node of the fiber tree. The further down the hierarchy, the higher the child nodes are.
RenderRootSync exits before resetting workInProgressRoot = null to indicate that no render is in progress. And mount the latest fiber tree onto fiberRoot. FinishedWork. At this point, the memory structure of the entire fiber tree is as follows (note the FiberRoot.finishedWork and Fiberroot.current Pointers, which are processed during the commitRoot phase):
conclusion
This section demonstrates the entire process of creating the fiber tree for the first time, tracking the memory reference changes during the process. The Fiber tree construction loop is responsible for constructing the new fiber tree, marking the fiber. Flags during the construction, and finally collecting all the flagged Fiber nodes into a side effect queue. The side effects of the queue was mounted to the root node (HostRootFiber. Alternate. FirstEffect). At this point, the Fiber tree and its DOM nodes are still in memory, waiting for the commitRoot phase to render.
Write in the last
This article belongs to the diagram react source code series in the operation of the core plate, this series of nearly 20 articles, really in order to understand the React source code, and then improve the architecture and coding ability.
The first draft of the graphic section has been completed and will be updated in August. If there are any errors in the article, we will correct them as soon as possible on Github.