Series article directory (synchronized update)
- React source code analysis series - React's render stage (1): Basic process
- React source code analysis series - React's render stage (2): beginWork
- React source code analysis series - React's render stage (3): completeUnitOfWork
- React source code analysis series - React's render exception handling mechanism
This series of articles discusses the source code of React v17.0.0-alpha
React workflow
The workflow of React is mainly divided into two stages: render and commit:
- The render phase is based on the ReactElement converted from JSX (more precisely, jsx is converted into a code segment wrapped by React.createElement() through babel in the code compilation phase, and the corresponding ReactElement is generated after the code segment is executed in the render phase. object) to create a Fiber tree; React adopts the idea of "double cache", so there are two Fiber trees at the same time, one is current , corresponding to the DOM tree currently rendered by the browser, and the other is workInProgress , is a working copy created by reconciler at initialization or after component state update.
- The commit phase refers to rendering workInProgress to the page. Of course, this process is not a full update, but determines what to do on a DOM node according to some "effectTag" (effectTag) printed when workInProgress is created. , such as updating text, deleting nodes, etc., to restore workInProgress on the DOM with as little cost as possible; workInProgress will be marked as current after commit.
Entry to the render phase
render phase begins performSyncWorkOnRoot or performConcurrentWorkOnRoot calling method, it is that the difference between these two methods is a synchronous, asynchronous and the other is ( Concurrent ) a.
These two methods respectively call the following two methods - workLoopSync and workLoopConcurrent:
function workLoopSync() {
// Already timed out, so perform work without checking if we need to yield.
while (workInProgress !== null) {
performUnitOfWork(workInProgress);
}
}
function workLoopConcurrent() {
// Perform work until Scheduler asks us to yield
while (workInProgress !== null && !shouldYield()) {
performUnitOfWork(workInProgress);
}
}
The difference between these two methods is whether to call shouldYield() to determine whether the current browser frame has free time. For concurrent mode, if there is no free time, the current loop will be exited; you can see that these two methods eventually Both call performUnitOfWork .
Through the while loop in the method, Fiber Reconciler will complete the creation of the entire workInProgress Fiber Tree through the strategy of " depth traversal ".
performUnitOfWork
function performUnitOfWork(unitOfWork: Fiber): void {
const current = unitOfWork.alternate; // current树上对应的Fiber节点,有可能为null
// ...省略
let next; // 用来存放beginWork()返回的结果
if (enableProfilerTimer && (unitOfWork.mode & ProfileMode) !== NoMode) {
// ...省略
next = beginWork(current, unitOfWork, subtreeRenderLanes);
// ...省略
} else {
next = beginWork(current, unitOfWork, subtreeRenderLanes);
}
// ...省略
unitOfWork.memoizedProps = unitOfWork.pendingProps;
if (next === null) { // beginWork返回null,表示无(或无需关注)当前节点的子Fiber节点
completeUnitOfWork(unitOfWork);
} else {
workInProgress = next; // 下次的workLoopSync/workLoopConcurrent的while循环的循环主体为子Fiber节点
}
// ...省略
}
Since Fiber Reconciler is refactored from Stack Reconciler, it actually simulates the "recursive" execution process, which is actually a process similar to "depth-first traversal". And performUnitOfWork is a specific method to control the "recursive" process, which mainly includes:
- "Delivery" stage - beginWork
- "Return" phase - completeUnitOfWork
the end condition of the recursion
Combined with the callers of performUnitOfWork - workLoopSync and workLoopConcurrent methods, as long as the pointer of the Fiber node of workInProgress is set to null, the entire recursive traversal ends.
function workLoopSync() {
// Already timed out, so perform work without checking if we need to yield.
while (workInProgress !== null) {
performUnitOfWork(workInProgress);
}
}
The "pass" phase of render - beginWork
In the "pass" stage, the method beginWork to generate a child Fiber node, and workInProgress will point to this new Fiber node as the main body of the next loop ( unitOfWork
); but according to the actual situation, beginWork may return null
, which is the termination condition of the recursion.
"Return" phase of render - completeUnitOfWork
Once beginWork returns null
, that is, the termination condition of the recursion is satisfied, then it enters the "return" stage of this loop - completeUnitOfWork
In the "return" stage, the method of completeWork is mainly called to collect the data on all descendant Fiber nodes (also called sub-fiber node tree) to the main body of this loop ( unitOfWork
)
Then, judge whether there is a sibling node, and if so, point the workInProgress pointer to the sibling node as the main body of the next loop ( unitOfWork
); otherwise, it will loop back to the parent node (of course, it will also be executed during the rollback process). completeWork method), until a sibling node is found to enter the next loop ( performUnitOfWork ), or the parent node is null
(at this time, the workInProgress pointer will point to null).
Note: the pointer variable workInProgress is located in the parent scope of completeUnitOfWork, so completeUnitOfWork method can assign the workInProgress pointer without return.
recursive completion
If the workInProgress pointer points to null in completeUnitOfWork , it means that the traversal has been completed to create the entire workInProgress tree.
At this point, all the work in the render phase is complete. In the performSyncWorkOnRoot function, the fiberRootNode is passed to the commitRoot method, and the commit stage is started.
An example of the recursive process
function App() {
return (
<p>
<span>array</span>
<span>huang</span>
</p>
)
}
ReactDom.render(<App />, document.getElementById('#root'));
1. rootFiber beginWork
2. App Fiber beginWork
3. p Fiber beginWork
4. span Fiber beginWork
5. span Fiber completeWork // 注意这里是直接走到 span 的“归”阶段,因为文本节点 "array" 被优化处理了
6. span Fiber beginWork
7. span Fiber completeWork
8. p Fiber completeWork
9. App Fiber completeWork
10. rootFiber completeWork
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。