As the size of a React project increases, if a certain node changes, it puts a huge pressure on the diff process. Comparing the truly changed parts through JavaScript consumes a significant amount of time, and JavaScript occupies the main thread to perform the diff, which may lead to the inability to render the page properly and cause lagging. To address the issue, the React team where rewrote the reconciliation algorithm and released it in React16. To differentiate it from the previous reconciler, it’s called the stack reconciler, while the new one is called the fiber reconciler, or simply Fiber. Essentially, Fiber can be understood as the virtual DOM after React16.
So far, there may be confusion between element, fiber, and DOM, as they are fundamentally different. The element object represents the JSX code and contains information such as props and children. On the other hand, the DOM represents the final visual output that the user sees. The fiber serves as a bridge between the element and the DOM. When an element changes, it goes through a reconciliation using the fiber, which ensures that the corresponding DOM is also updated.
When JSX is compiled by Babel, it will always go through the beginWork
method.
function beginWork(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
): Fiber | null {
// ...
switch (workInProgress.tag) {
case IndeterminateComponent: {
}
// ...
}
}
By using the tag of workInProgress, determine the fiber corresponding to the element. The three parameters of beginWork correspond to:
current
: The current Fiber node representing the view tree of the current component, which was last updated.workInProgress
: The current component corresponds the Fiber node, and all updates occur in the workInProgress, which will eventually replacecurrent
.- renderLanes: This is related to priority. In React18, there is an important concept called concurrency. Since JavaScript is a single-threaded language, it can only perform one task at a time. If a task takes too long to complete, it can block the execution of subsequent tasks. Therefore, React wants to prioritize the execution of non-blocking tasks when a long-running task is pending. To achieve this, it needs to determine which tasks are time-consuming, how to interrupt tasks, and how to resume after interrupted.
As a result, the concept of time slicing was introduced. React divides tasks into smaller time slices, allowing them to run independently and be processed in batches, transforming synchronous tasks into interruptible asynchronous tasks. In most browsers, there is a built-in method called requestIdleCallback
that allows a callback function to be called during browser idle periods. However, due to browser compatibility issues and unstable triggering frequency, React abandoned this method and implemented their own more comprehensive requestIdleCallback polyfill
, which supports setting multiple scheduling priorities. Each update is assigned a priority, and non-urgent updates are divided into time slices and allocated to browser frames. Higher priority updates will interrupt lower priority updates. Once the higher priority tasks are completed, the lower tasks will be executed.