The concept has been revealed in 2016, which is 202 years old. I started writing Fiber today. However, the good thing is that there are abundant materials about Fiber. There are many reference materials when WRITING the article, so it is easy to understand deeply.
React is my favorite framework, and I would like to spend a lot of time to learn it well. I found that there are four feelings about learning a framework. I didn’t use it at the beginning, so I might have a magical feeling. Then contact, encounter unfamiliar grammar, feel this is rubbish, this is not anti-human? And then when you get used to it, it’s nice, it’s nice, and it changes the way you think about programming; To later, read his source code, understand his design, the design is really good, feel that they can write a look.
So I’m going to write down some of the things that newbies don’t know when using the API, and some of the reasons why things are designed the way they are, to explore the secrets of React.
My idea is to introduce from top to bottom, first understand the overall Fiber architecture, and then dig into each point, so this article is mainly about Fiber architecture.
introduce
Before getting into the details of Fiber, let’s take a look at what Fiber is and why the React team spent two years refactoring the coordination algorithm.
The core idea of React
A virtual DOM tree is maintained in memory. When the data changes (setState), the virtual DOM is automatically updated and a new tree is obtained. Then, the old and new virtual DOM trees are Diff and the changed parts are found to obtain a Change(Patch), and the Patch is added to the queue. Finally, batch update these patches into DOM.
React 16 pre-react deficiencies
React has two main phases: render() and setState()
Reconciler: The Reconciler’s official interpretation. React recursively generates a new Virtual DOM by traversing the new data from the top down. Then, Diff algorithm is used to find patches that need to be changed and place them in the update queue.
Renderer: Iterates through the update queue to actually update render elements by calling the host environment’s API. Host environments such as DOM, Native, WebGL, etc.
In the coordination phase, it is also called a Stack Reconciler because of the recursive manner in which it is passed, mainly to distinguish the name given to the Fiber Reconciler. This approach has one characteristic: Begin once the task cannot be interrupted, so js will always occupy the main thread, always have to wait until after the completion of the whole Virtual DOM tree in computing to the executive power to the rendering engine, then this will lead to some user interaction, animation and so on task cannot be processed immediately, there will be caton, the influence of the user experience very much.
How to solve the previous deficiencies
The main problem is that once the task is executed, it cannot be interrupted. The JS thread has been occupying the main thread, resulting in a lag.
May be some contact front end soon do not understand why js has been occupying the main thread will be stuck, I here or simple popularization.
What does the browser need to do for each frame?
The page is drawn frame by frame, and the page is smooth when the number of frames drawn per second (FPS) reaches 60, below which the user feels stuck.
1S is 60 frames, so the time allotted to each frame is 1000/60 ≈ 16 ms. So we try to write code that doesn’t take more than 16ms per frame.
Browser work within a frame
As can be seen from the figure above, tasks of the following six steps need to be completed within a frame:
- Handle user interactions
- JS parsing execution
- Start frame. Window size change, page roll away, etc
- rAF(requestAnimationFrame)
- layout
- draw
If any of these six steps takes too long, after a total of more than 16ms, the user may see a lag.
However, if the harmonization phase mentioned in the previous section takes too long, that is, the JS execution time is too long, then it is possible that when the user has interaction, it should have rendered the next frame, but js is still executed in the current frame, which leads to the user interaction can not trouble to get feedback, resulting in the sense of lag.
The solution
** Break the render update process into multiple sub-tasks, do a small part at a time, see if there is any time left, if there is any move to the next task; If not, suspend the current task, give time control to the main thread, and continue executing it when the main thread is less busy. ** This strategy is called Cooperative Scheduling, and it is a common task Scheduling strategy for operating systems.
Supplementary knowledge, commonly used task scheduling strategies of operating systems: first come first service (FCFS) scheduling algorithm, short job (process) first scheduling algorithm (SJ/PF), highest priority first scheduling algorithm (FPF), high response ratio first scheduling algorithm (HRN), time slice rotation method (RR), multi-level queue feedback method.
Cooperative scheduling is mainly used to allocate tasks. When an Update task comes, it does not immediately perform Diff operation. Instead, it sends the current Update to an Update Queue and sends it to the Scheduler to process. Scheduler processes the Update based on the current main thread usage. To implement this feature, the requestIdelCallbackAPI is used. React adds Pollyfill to browsers that don’t support this API.
We have already seen that the browser executes frame by frame, and the main thread usually has a small Idle Period between the two execution frames. RequestIdleCallback can call IdleCallback during this Idle Period to perform some tasks.
- Low priority tasks are performed by
requestIdleCallback
Processing; - High priority tasks, such as animation related by
requestAnimationFrame
Processing; requestIdleCallback
Idle period callbacks can be invoked in multiple idle periods to perform tasks;requestIdleCallback
Method to provide deadline, that is, task execution limit time, in order to split tasks, avoid long time execution, blocking UI rendering and resulting in frame drop;
This solution seems really good, but how to implement it may encounter several problems:
- How to break it down into subtasks?
- How big is a subtask?
- How do you tell if you have time left?
- How to schedule which tasks should be executed with the remaining time?
- What if there is no time left for the previous task?
The entire Fiber architecture is designed to address these issues.
Please pay attention to my zhihu column (constantly updated), here, I will put all important articles in this directory for you to read, I hope to be useful to you
Ali factory standard Web front end senior engineer tutorial directory, from the basic to the advanced, look to ensure that your salary rises a step
Here I prepared a lot of learning materials for you, I hope to be useful to you on the way of learning
That’s the only thing that separates you from Ali engineers
What is the Fiber
In order to solve the problems encountered by the previously mentioned solution, the following goals were proposed:
- Pause and come back later.
- Assign priorities to different types of work.
- Reuse previously completed work.
- If it is no longer needed, abort the work.
To do this, we first need a way to break down tasks into units. In a sense, this is Fiber, and Fiber stands for a unit of work.
But simply breaking up into units won’t do it either, because that’s what a function call stack looks like. Each function has a job, and each job is called a stack frame, and it keeps working until the stack is empty and can’t be interrupted.
So we need an incremental rendering schedule, and then we need to re-implement a stack frame schedule that can execute them according to its own scheduling algorithm. In addition, since these stacks are self-controlled, concurrency or error bounds can be added.
Therefore, Fiber is a reimplemented stack frame. In essence, Fiber can also be understood as a virtual stack frame, which breaks the interruptible tasks into multiple sub-tasks. By scheduling sub-tasks according to their priorities and updating them in segments, the previous synchronous rendering is changed to asynchronous rendering.
So Fiber is a data structure (stack frame) and a solution for interruptible calling tasks. Its features are time slicing and Supense.
For those of you who know coroutines, Fiber’s solution is a bit like a coroutine (but very different) in that you can interrupt and control the order of execution. Generator in JS is actually a way of using coroutines, but with smaller granularity, it can control the sequence of code calls in functions and can also be interrupted.
How does Fiber work
ReactDOM.render()
和setState
To start creating updates.- The created updates are queued for scheduling.
- Perform tasks when requestIdleCallback is idle.
- The Fiber Node is traversed from the root Node and the WokeInProgress Tree is built.
- Generate effectList.
- Update the DOM based on the EffectList.
Below is a detailed diagram of the execution process:
- Part I from
ReactDOM.render()
The React Element method starts by converting the received React Element to a Fiber node, prioritising it, creating an Update, and adding it to the Update queue. - The second part mainly consists of three functions:
scheduleWork
,requestWork
,performWork
React 16 allows you to make asynchronous calls. React 16 allows you to make asynchronous callsPhase of the Schedule, the Cooperative Scheduling mentioned above is in this stage, and the third part will continue to be executed only after this solution obtains an executable time slice. React scheduling is the key process of React scheduling. - The third part is a large loop, traversing all Fiber nodes, calculating all update work through Diff algorithm, and producing EffectList for use in the COMMIT stage. The core of this part is the beginWork function. This is basically the Fiber Reconciler, which includes the reconciliation and commit phases.
Fiber Node
FIber nodes, which carry critical contextual information through the creation and update process, list important FIber fields grouped together.
{... StateNode: any, // Single list tree structurereturn: Fiber | null, / / to him in the Fiber in the node tree ` parent `, used to back up after finish this node child: Fiber | null, / / to her first child node (: Fiber | null, structure / / to his brother, brother nodesreturn// Update related pendingProps: any, // memoizedProps: any, // Update related props memoizedProps: any, // UpdateQueue < any > | null, / / the corresponding components of Fiber memoizedState Update will be stored in the queue: Any, // Last render state // context context expirationTime: ChildExpirationTime, // indicates when a task should be completed in the future, excluding tasks generated by its subtree. // In the context of Fiber tree update, Each Fiber has a corresponding Fiber // we call it 'current <==> workInProgress' // After rendering they alternate: Fiber | null, / / Effect related effectTag: SideEffectTag, / / used to record Side Effect nextEffect: Fiber | null, / / singly linked list is used to quickly find the next side effect firstEffect: Fiber | null, / / subtree first side effect lastEffect: Fiber | null, / / the last one in the subtree side effect... };Copy the code
Fiber Reconciler
In The second part, after Schedule is completed and time slices are retrieved, reconcile begins.
Fiber Reconciler is the Reconciler in React, which is also the process of how to execute each task and update each node after task scheduling is completed, corresponding to the third part above.
The reconcile process is divided into two phases:
- (Interruptible) Render/Reconciliation constructs the WorkInProgress Tree to get Change.
- Commit Applies these DOM changes.
Reconciliation phase
During the Reconciliation phase, one Fiber is processed at a time, after which the entire working cycle can be interrupted/suspended. The task results are collected by merging the Effect List upward at the end of each node update. After reconciliation, all Side effects including DOM change are recorded in the Effect List of the root node.
The render phase can be understood as a Diff process, resulting in Change(Effect List), which executes a declaration cycle method declaring the following:
- [UNSAFE_]componentWillMount
- [UNSAFE_] componentWillReceiveProps (deprecated)
- getDerivedStateFromProps
- shouldComponentUpdate
- [UNSAFE_]componentWillUpdate (deprecated)
- render
Since the Reconciliation phase is interruptible and will be executed again once it is resumed, it is likely that the life cycle methods of the Reconciliation phase will be called many times, so the methods of the life cycle of the Reconciliation phase will be unstable. I think this is also why the React to scrap componentWillMount and getDerivedStateFromProps componentWillReceiveProps method to static methods.
The commit phase
The COMMIT phase can be understood as the process of reflecting the results of the Diff into the real DOM.
In the commit stage, the commitRoot will insert, update and delete the effectTag of effect according to the effectTag, which can be found in the source code. Different update methods will be called according to the tag.
The commit phase performs the following declaration cycle methods:
- getSnapshotBeforeUpdate
- componentDidMount
- componentDidUpdate
- componentWillUnmount
P.S. : Pay attention to the difference between reconciler, Reconciler, and Reconciliation. It is a reconciler, a noun, and a module of React’s work, a coordination module. Reconcile is the act of reconciling with a conciliator. It is a verb. Reconciliation is just the first stage of the reconciliation process.
Fiber Tree and WorkInProgress Tree
React creates a Virtual DOM Tree using the React. CreateElement method. Fiber is added to the Tree to record context information. Each Element corresponds to a Fiber Node, and the structure linking Fiber nodes is called Fiber Tree. It reflects the state of the application used to render the UI. This tree is often referred to as the current tree (the current tree, which records the state of the current page).
In subsequent updates (setState), each re-rendering recreates the Element, but Fiber does not. Fiber only updates its necessary properties with data from the corresponding Element.
An important feature of Fiber Tree is its linked list structure, which iterates through the recursive traversal programming loop and then implements task splitting, interruption, and recovery with the requestIdleCallback API.
The structure of this link is mainly composed of the following fields of the previous Fiber Node:
// Single list tree {return: Fiber | null, / / points to the parent node child: Fiber | null, / / to her first child node (: Fiber | null, structure of / / to his brother, brother nodesreturnPointing to the same parent node}Copy the code
Each Fiber Node corresponds to the Virtual Dom one by one. All Fiber nodes are connected to form a Fiber tree, which is a single-linked list tree structure, as shown in the figure below:
Fiber Tree is a single-linked list of nodes connected to each other.
There is a single linked list when render is calledsetState
How did Diff get change?
Using a technique called double buffering, another Tree is required: the WorkInProgress Tree, which reflects the future state to be refreshed to the screen.
If the current pointer points to the WorkInProgress Tree and the old Fiber Tree is discarded, the new Fiber Tree is created.
The benefits of this:
- Ability to reuse internal objects (Fiber)
- Save time for memory allocation and GC
- Even if there’s an error running, it doesn’t affect the data on the View
Each Fiber has an alternate property that also points to a Fiber. Alternate is preferred when creating a WorkInProgress node, or if there is none.
The process of creating the WorkInProgress Tree is also a Diff process, which generates an Effect List that is used to handle side effects in the final Commit phase.
conclusion
Originally, I wanted to explain Fiber thoroughly in an article, but I found that there were too many words to write. If I wanted to write in detail, IT was estimated that I would write tens of thousands of words. Therefore, THE purpose of this article is just to sort out the general workflow of React without involving the source code. Details such as how to schedule asynchronous tasks, how to do Diff, and so on will be analyzed in sections with the source code.