Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

In-depth understanding of React Fiber mechanism #33

Open
hawx1993 opened this issue Jan 4, 2023 · 0 comments
Open

In-depth understanding of React Fiber mechanism #33

hawx1993 opened this issue Jan 4, 2023 · 0 comments

Comments

@hawx1993
Copy link
Owner

hawx1993 commented Jan 4, 2023

引言

Before getting to know FIber in depth, we can first understand the development history of React architecture:

The React15 architecture can be divided into two layers:

  • Reconciler - Responsible for identifying components that have changed
  • Renderer (renderer) - responsible for rendering the changed components to the page;

The React16 architecture can be divided into three layers.

  • Scheduler - priority of scheduling tasks, with high-priority tasks going to Reconciler.
  • Reconciler - responsible for finding out which components have changed:** updating has changed from a recursive to a circular process that can be interrupted. The Reconciler uses Fiber's architecture internally**.
  • Renderer - responsible for rendering the changed components to the page.

Why React Officially Launched Fiber

The Fiber architecture was developed by React to solve some performance problems in React and to provide some new features.

One of the main goals was to solve React's "single-threaded rendering" problem. In React's traditional architecture, the entire rendering process is performed on a single JavaScript thread, which means that if there are many large components in the component tree, then long blocks can occur during the rendering process. This can lead to performance issues and can have a negative impact on the user interface.

The Fiber architecture solves this problem by using co-processing. Co-processing allows the rendering of components to be paused and restarted during the rendering process, which allows higher priority components to be prioritized, thus improving performance.

In addition, the Fiber architecture offers many other new features, including better animation support, better browser event handling, better synchronous and asynchronous rendering support, and better accessibility support.

Off-topic: What is the Fiber architecture'scoroutines

The Fiber architecture uses coroutines to optimize the rendering process of React applications. In the Fiber architecture, each component corresponds to a coroutines, and it is possible to pause and restart the rendering of a component during the rendering process. This allows prioritizing components with higher priority, thus improving performance.

The Fiber architecture's concurrency is implemented by using JavaScript's Generator function

The Generator function is a special function that pauses and saves the current execution state while it is being executed. This makes it possible to pause the function at any time during execution and resume execution at a later time. In the Fiber architecture, each component corresponds to a Generator function that is called during the rendering process and pauses and resumes execution when necessary.

Note that the Fiber architecture's concurrency is not a true multi-threaded concurrency, but rather simulates the behavior of multiple threads on a single JavaScript thread. However, it can achieve multi-thread-like effects without creating new threads, and can improve performance in some cases.

So, what is Fiber?

React Fiber can be thought of as a re-implementation of React's core algorithm. So how do we understand Fiber in React?
We can understand it on two levels.

  • From the operation mechanism point of view, Fiber is a process yielding mechanism that enables interruptible rendering and gives back control of rendering to the browser, thus achieving the purpose of not blocking browser rendering
  • From the perspective of data structure, Fiber is a data structure (Linked list), an execution unit

Off-topic: what does a linked list look like

image.png

As shown above, where memoizedState is where we store the hooks data. It is a linked list linked by next.

So, what is Fiber mainly used to solve?

In React15 and before, a recursive approach is used to create the virtual DOM, and the recursive process cannot be interrupted. If the component tree is very deep in the hierarchy, recursion will take up a lot of time in the thread and cause lag.

As we know, the browser assigns single thread to js, and JS and UI threads are mutually exclusive. Whenever a JS thread is executed, the UI thread will be hung and wait for the JS execution to finish before continuing. Therefore, the user will perceive the page lag situation.

Therefore, React Fiber introduces dynamic priority and interruptible rendering

  • Dynamic priority: In the old version of React, "reconciliation" was done all at once and all the work had the same priority. This could lead to poor performance when dealing with large or complex UIs. React Fiber introduces the concept of dynamic priority, which allows React to adjust the priority based on the current state for improved performance.
  • Interruptible rendering: In the old version of React, "reconciliation" was a one-way process that couldn't be stopped once it started. This meant that if a block occurred during the rendering process, such as needing to wait for a network request, the entire UI would be suspended until the block completed. React Fiber allows for pausing and resuming the rendering process to avoid blocking the entire UI

By addressing these issues, React Fiber improves the performance of UI rendering, especially when dealing with large or complex UI. It also introduces many new features to React, such as animation optimization and asynchronous rendering.

So, what is dynamic prioritization?

Dynamic prioritization is a new concept introduced by React Fiber that allows React to adjust the priority of work based on the current state during "reconciliation". This can be done by adjusting the "weight" of each task.

In React Fiber, each task has a weight value that indicates its priority. For example, a task may have a higher weight than another task, which means it will proceed before the latter. The weight value can be positive or negative, and a larger weight value indicates a higher priority.

React Fiber uses these weight values to determine which tasks should be executed earlier, thereby improving performance. For example, if there is a long-running task, it can be set to a lower weight value so that other tasks can be completed first. This allows the browser to respond to user input faster, improving the user experience.

Dynamic prioritization is implemented by React Fiber by tracking the current state and constantly adjusting the priority. It can be controlled through React's "scheduleUpdate" method, which allows developers to set weight values for specific components. This allows developers to more precisely control React's "reconciliation" process to optimize performance.

The previous version of react used the expirationTime property to represent the priority, the priority and IO can not work well together (the priority of io is higher than the priority of the cpu), now there is a more fine-grained priority representation Lane, Lane with a binary bit to represent the priority, the 1 in binary indicates the position, the same binary number can have more than one bit of the same priority, which can represent the concept of 'batch', and the binary is convenient to calculate.

Similar to a racing lane, the closer to the inner circle the shorter the track and the closer to the outer circle the longer the track. react represents 31 tracks by 31 bits of binary, the smaller the number of bits the higher the priority of the track.

So, how does React achieve an interruptible rendering process?

After talking about dynamic priorities, let's dive into what interruptible rendering is and how React achieves interruptible rendering process?

In React Fiber, the rendering process is done in "frames". Each frame is a period of time during which React can perform some work and then return control to the browser. This allows the browser to process user input or perform other tasks during the rendering process, thus avoiding blocking the entire UI.

In each frame, React performs a number of "tasks", such as rendering components, calling lifecycle methods, executing data requests, etc. React determines the order of execution based on the weight value (dynamic priority) of each task.

When React is executing a task, if it finds that the execution of a task is longer than the time remaining in the current frame, it pauses and resumes execution in the next frame. This is how React Fiber implements the pause and resume of the rendering process.

In this way, React can keep the browser responsive during rendering and avoid blocking the entire UI. this can improve the user experience, especially when dealing with large or complex UI.

So, how did Fiber manage to cede control?

In building and updating the user interface, React Fiber processes tasks in batches and yields control between batches so that the browser can perform other actions. This allows React Fiber to perform a large number of tasks without blocking the browser and improves the performance of UI rendering

React Fiber uses the technique of yielding control to improve the performance of UI rendering, but this does not mean it will always yield control. When the browser has ample CPU resources available, React Fiber can continue to execute tasks without yielding control in order to complete the UI rendering as quickly as possible.

In React, the technique of yielding control is typically implemented using the browser's requestIdleCallback function (react has implemented its own version of requestIdleCallback using MessageChannel + requestAnimationFrame).

"This function allows a callback function to be called when the browser is idle and allows specifying how much CPU time can be used at most. React Fiber can use this function to execute tasks during browser idle times, yielding control."

Why did React implement its own version of requestIdleCallback??

Why not setTimeout?

Because if the recursive level of setTimeout is too deep, the delay will not be 1ms but 4ms, which can cause a long delay

Why not requestAnimationFrame?

The requestAnimationFrame is executed after the microtask is executed and before the browser rearranges and redraws, and the timing of the execution is inaccurate. If the execution time of JS before raf is too long, it will still cause a delay.

Compared with setTimeout, the biggest advantage of requestAnimationFrame is that the system determines the timing of the callback function execution. (If the screen refresh rate is 60Hz, then the callback function is executed once every 16.7ms)

Why not requestIdleCallback?

The execution timing of requestIdleCallback is executed after the browser rearranges and redraws, which is the idle time of the browser. In fact, the timing of execution is still inaccurate.

Why MessageChannel?

First, MessageChannel is executed earlier than setTimeout. Second, requestIdleCallback is not supported by all browsers. To solve this problem, React uses MessageChannel to emulate requestIdleCallback.

image.png

image.png

React uses it to emulate the behavior of a requestIdleCallback, such as performing UI updates in the main thread and other compute-intensive tasks in the worker thread.

"In this case, React can perform UI updates on the main thread and non-UI tasks on a worker thread. This avoids blocking the main thread and improves performance."

MessageChannel pr reference:Use setImmediate when available over MessageChannel

Why does Fiber have to be a linked list, can't it be an array?

The reason Fiber uses a linked list data structure is because a linked list allows for easy insertion and removal of elements in the middle of a list. This is useful when building and updating a user interface, as there may be a large number of elements that need to be inserted or removed.

Linked lists have better insertion and deletion performance compared to arrays because performing these operations on arrays typically requires moving a large number of elements, while in linked lists only a few pointers need to be modified.

Disadvantages of linked lists: However, linked lists often have worse search performance compared to arrays because the entire list must be traversed to find the desired element.

Despite this, Fiber still chooses to use a linked list as its data structure because the need for inserting and deleting elements during the building and updating of a user interface is much greater than the need for searching elements.

So, why Fiber can improve performance?

From the above conclusions:

  • Fiber's double buffering allows for a switch from the wip Fiber tree to the current Fiber after it has been built, making the switch in memory all at once and improving performance
  • The existence of Fiber makes asynchronous, interruptible updates possible. As a unit of work, it can perform work within a time slice and then yield control back to the browser when the time is up. When the next time slice begins, the Fiber that was paused can resume where it left off.
  • Fiber can perform the necessary diff updates during reconciliation, allowing the final updates to be applied to the actual nodes.

So, what is the difference between Fiber Reconciliation and DOM Diff

"DOM diff" usually refers to operations performed in the browser, while React's "reconciliation" occurs in the application's JavaScript code.

There are several advantages when using React's "reconciliation" for virtual DOM comparisons and updates:

  • It can efficiently update specific parts of the page rather than the entire page, avoiding unnecessary work and improving performance.
  • It can be done in JavaScript code rather than in the browser, meaning you can use all the power of JavaScript to write your page update logic and use it on the server-side to generate HTML.
  • It uses virtual DOM technology, which means you can describe the structure of the page with simple JavaScript objects rather than manually updating the DOM. This can make your code more concise and maintainable.

Why React Reconciliation Makes Performance Better?

We mentioned earlier that in React15, the virtual DOM is created through recursion, which is an uninterruptible process. This can lead to performance issues with lag. So, how does React's reconciliation improve performance?

In React, when a component's state changes, the component will be re-rendered. However, sometimes a component's state may change but the component does not actually need to be re-rendered. For example, when a parent component's state changes, it may cause the state of a child component to also change, but if the child component's view has not truly changed, then there is no need to re-render it.

Reconciliation solves this problem by "reconciling" the components. When the state of a parent component changes, reconciliation will first re-render the parent component. It will then "reconcile" the child components to determine which ones actually need to be updated. This way, unnecessary re-renders of components can be avoided, improving performance.

Reconciliation also offers some other optimizations such as "cascading updates," which involves batch updating multiple components in a single re-render. This can reduce the number of times the browser needs to repaint, further improving performance.

Overall, React reconciliation is an effective optimization technique that helps developers improve performance while maintaining the maintainability of the application. It achieves this by reducing unnecessary re-renders of components and optimizing the update process.

Fiber Vs Stack Demo

From the comparison of the above two demos, we can see that Fiber's handling of animation is smoother.

So, how does the Fiber architecture better implement animation support?

React Fiber offers a new way to implement animations called the "requestAnimationFrame" mechanism. This mechanism allows for the processing of animations before the browser's next repaint.

In the traditional architecture of React, animations are implemented using setInterval or setTimeout, which means the frame rate of the animation may not be very stable and it can take up a lot of CPU time.

Instead, the requestAnimationFrame mechanism in the Fiber architecture better controls the frame rate of the animation and doesn't take up as much CPU time. This means that the animation can be smoother and won't have as big of an impact on performance in larger applications.

In addition to improving animation performance, the Fiber architecture also offers a new way to implement animations called "animation scheduling." This mechanism allows developers to have more fine-grained control over the timing of the animation and can make the animation smoother.

What is the difference between the Fiber architecture and React's traditional architecture?

React Fiber is an upgraded version of React's architecture, which has some significant differences from React's traditional architecture:

  • The Fiber architecture is based on coroutines, while traditional architectures are based on stacks. This means that the Fiber architecture can more flexibly manage the rendering process of components and can better handle animations and other asynchronous tasks.
  • The Fiber architecture can better control frame rates because it uses the requestAnimationFrame mechanism to implement animations. This means that animations can be smoother and will not take up too much CPU time.
  • The Fiber architecture provides a new mechanism called "animation scheduling" that can more finely control the timing of animations and make them smoother.
  • The Fiber architecture allows for the pausing and resuming of component rendering during the rendering process, which allows for higher priority components to be processed first, improving performance.

Although there are many differences between Fiber architecture and traditional architecture, the differences between them are not obvious to developers. In most cases, developers can continue to use React's standard way of developing applications without knowing the underlying implementation details.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant