1

Students who use React should know React Fiber, because this thing is about to be officially released.

Facebook has been tossing about the big change of React Fiber for more than two years. At the React Conf 2017 meeting that just ended, Facebook finally confirmed that React Fiber will be released on the ride of the next major version of React, v16.

Is Fiber Ready Yet? . What I just saw (1:41 pm on March 28, 2017, Beijing time) is 93.6%. It is said that Facebook is in its own 'S website has put React Fiber into actual combat, so, the general trend of React Fiber, it's time to learn about React Fiber.

What is React Fiber? The official one-sentence explanation is " React Fiber is a re-implementation of the core algorithm ". It seems too vague to say that, so I still have to elaborate.

First of all, don't be too nervous, don't think that the arrival of React Fiber is a great revolution. In fact, for developers who only use React as a tool, it is very likely that we will not feel any functional changes. When React v16 is released, we modify the react version number in package.json, re-npm install, everything is done, and then we feel that the web page performance is higher, that's it.

Of course, the above is the "ideal situation", React Fiber's modification of the algorithm will still bring some functional logic changes, which will be discussed later.

Why does Facebook want to engage in React Fiber? We must first understand the current limitations of React (that is, up to the latest v15 version).

Limitations of the synchronization update process

In existing React, the update process is synchronous, which may cause performance issues.

When React decides to load or update the component tree, it will do a lot of things, such as calling the life cycle functions of each component, calculating and comparing the Virtual DOM, and finally updating the DOM tree. The whole process is synchronous, which means that there is only one When the loading or updating process starts, React will run to the end with one effort and never stop in the middle of it.

On the surface, this design is quite reasonable, because the update process will not have any I/O operations, it is completely CPU calculations, so there is no need for asynchronous operations. It is true that you only need to run all the way, but when the component tree is relatively large At that time, the problem came.

If it takes 1 millisecond to update a component, if there are 200 components to be updated, it takes 200 milliseconds. During the 200 millisecond update process, the only main thread of the browser is concentrating on running the update operation, and there is no time to do anything else. Things. Imagine that in these 200 milliseconds, if the user enters something into an input element, the keyboard will not get a response, because rendering the input key results is also the job of the browser's main thread, but the browser's main thread is occupied by React. , I can’t take out the space. The final result is that the user can’t see the response after pressing the button. After the React update process is over, the buttons suddenly appear in the input element.

This is the so-called interface freeze, which is a very bad user experience.

In the existing version of React, this problem occurs when the component tree is very large, because the update process is synchronized layer by layer and layer by layer. The process of gradual deepening does not stop until all the components are updated. The function is The call stack is as shown in the figure below. The call is very deep and will not return for a long time.

image.png

Because of the single-threaded nature of JavaScript, each synchronization task does not consume energy for too long, otherwise the program will not respond to other inputs. The update process of React is guilty of this taboo, and React Fiber is about changing the status quo.

React Fiber way

The method to crack the excessively long synchronization operation in JavaScript is actually very simple-sharding.

Divide a time-consuming task into many small pieces. The running time of each small piece is very short. Although the total time is still very long, after each piece is executed, other tasks are given a chance to execute, so that the only thread is not Will be monopolized, other tasks still have the opportunity to run.

React Fiber fragments the update process. The execution process is as shown in the figure below. After each update process is executed, the control is returned to the module responsible for task coordination in React to see if there are other urgent tasks to be done, if not, then Continue to update, if there is an urgent task, then do the urgent task.

Maintaining the data structure of each shard is the Fiber.

With the fragments, the call stack of the update process is shown in the following figure. Each trough in the middle represents the execution process of a certain fragment, and each crest is the time when the execution of the fragment is finished and control is returned.

image.png

The reason is very simple, but it is not easy for React to achieve this, otherwise how to toss for more than two years!

Students who are interested in specific data structure principles can go to see Lin Clark's speech (by fanqiang) at React Conf 2017. The introduction pictures in this article are also from this speech.

How Fiber Asynchronous Rendering

When doing display work, I often hear a target called 60 frames, which indicates the update frequency of the screen, that is, the screen is updated 60 times per second. This is because at the update frequency of 60 frames, the page looks smooth in the eyes of humans, and there is no obvious lag. Update 60 times per second, that is, the page needs to be updated every 16ms. If the time to update the page is less than 16ms, then there will be a little time left to perform other tasks before the next update time comes, as long as the interval of 16ms is ensured in time The next update interface will not affect the smoothness of the page at all. The core of fiber uses the 60-frame principle to implement a cyclic task scheduling algorithm based on priority and requestIdleCallback.

image.png

requestIdleCallback is an api provided by the browser, which allows the browser to execute a callback when it is idle. The remaining time of the current frame can be obtained in the callback parameter. The fiber uses this parameter to determine whether the current remaining time is enough to continue the task. , If it is enough, continue execution, otherwise suspend the task, and call requestIdleCallback to notify the browser to continue executing the current task when it is idle.

function fiber(剩余时间) {
 if (剩余时间 > 任务所需时间) {
 做任务;
 } else {
 requestIdleCallback(fiber);
 }
}

Fiber also sets different priorities for different tasks. High-priority tasks need to be displayed on the page immediately. For example, when you are entering text in an input box, you definitely want your fingers to press each key on the keyboard. , The input box can immediately give feedback so that you can know whether your input is correct and valid. Low-priority tasks are like some data coming from the server. At this time, the page needs to be updated. For example, the number of people who like this article is +1 or the comment is +1. This is not such an urgent update, and the delay is 100-200ms. There will not be much difference, and it can be handled later. Fiber dynamically adjusts task scheduling according to task priority, and completes high-priority tasks first.

{ 
 Synchronous: 1, // 同步任务,优先级最高
 Task: 2, // 当前调度正执行的任务
 Animation 3, // 动画
 High: 4, // 高优先级
 Low: 5, // 低优先级
 Offscreen: 6, // 当前屏幕外的更新,优先级最低
}

In the fiber architecture, there is a data structure, its name is fiber, which is why the new reconciler is called fiber. A fiber is actually a js object. The more important attributes of this object are stateNode, tag, return, child, sibling, and alternate.

Fiber = {
 tag // 标记任务的进度
 return // 父节点
 child // 子节点
 sibling // 兄弟节点
 alternate // 变化记录
 .....
};

We can see that fiber is based on a linked list structure and has pointers to its parent nodes, child nodes and sibling nodes. During the diff process, it traverses according to the relationship of node connections.

Why is it called Fiber?

Everyone should be aware of the concepts of Process and Thread. In computer science, there is another concept called Fiber, which means "fiber" in English, which means a thread that is thinner than Thread, that is, more than thread. A more sophisticated concurrency processing mechanism.

The above-mentioned Fiber and React Fiber are not the same concept, but I believe that the React team named this function Fiber, which means a closer processing mechanism, which is more detailed than Thread.

To make a digression, many years ago, I listened to a senior who gave a lecture. He said about Fiber, so I asked him: How can I make my program be able to control Fiber? The answer from the predecessor is: Does your program really need to use Fiber? If the current method can meet the demand, there is no need to know what Fiber is.

Haha, I didn’t quite understand what the senior said at the time. Later, I became more and more convinced that the senior said it makes sense. If there is no need to use something at all, then don’t use it. Don’t use it just because it’s cool, otherwise it’s very likely. Ask for hardship.

Far away, what I want to say is, in fact, for most React users, there is no need to delve into how React Fiber is implemented. Unless the implementation method really affects our way of using it, we don’t need to learn how to use the bun. Don’t eat buns when you make them, right?

However, the changes in the implementation of React Fiber really require some adjustments to our code methods.

The impact of React Fiber on existing code

Ideally, React Fiber should not affect the existing code at all, but it is a pity that it is not at all. If you want to eat this bun, you really need to know a little bit about how this bun is made. If you don’t like sweet ones, don’t eat sugar buns, yes. wrong?

In React Fiber, an update process will be divided into multiple shards to complete, so it is entirely possible that an update task has not been completed before it is interrupted by another higher priority update process. At this time, the higher priority update task It will be processed first, and the work done by the low-priority update task will be , and then wait for the opportunity to start .

Because an update process may be interrupted, an update process of React Fiber is divided into two phases (Phase): the first phase Reconciliation Phase and the second phase Commit Phase.

In the first phase of the Reconciliation Phase, React Fiber will find out which DOM needs to be updated. This phase can be interrupted; but in the second phase of the Commit Phase, the DOM will be updated in one effort and will never be interrupted.

Most of the work in these two stages is done by React Fiber, and the life cycle function that is relevant to us.

Taking the render function as the boundary, the following life cycle functions may be called in the first stage. It is said that it is "may be called" because the functions called in different life cycles are different.

  • componentWillMount
  • componentWillReceiveProps
  • shouldComponentUpdate
  • componentWillUpdate

The following life cycle functions will be called in the second phase.

  • componentDidMount
  • componentDidUpdate
  • componentWillUnmount

image.png

Because the process of the first stage will be interrupted and "start over", it will cause unexpected situations.

For example, a low-priority task A is being executed, and the componentWillUpdate function of a certain component has been called, and then found that its time slice has been used up, so it pops up to see if there is an urgent task, oops, really There is an urgent task B, and then React Fiber will execute this urgent task B. Although task A is halfway through, it has no choice but to give up completely. After task B is completed, task A starts again. Pay attention , Is to start again, not from the middle part just now, that is to say, the componentWillUpdate function will be called again.

In the existing React, each life cycle function is definitely only called once during a load or update process; is no longer the case in React Fiber. The life cycle function in the first stage is loaded and updated once. It may be called multiple times during the update process!

image.png

After using React Fiber, be sure to check these life cycle functions related to the first phase to see if there is any logic that assumes that it is only called once during an update process, and if there is, it will be changed.

Let's take a look at these functions that may be called repeatedly.

  • ComponentWillReceiveProps, even if the current component is not updated, this function will be called as long as the parent component is updated, so it’s okay to call it a few more times, pass!
  • shouldComponentUpdate, the function of this function is to return a true or false, there should not be any side effects, there is no harm in calling it a few times, pass!
  • render, it should be a pure function, it doesn't hurt to call it a few more times, pass!

Only two functions, componentWillMount and componentWillUpdate, often contain side effects, so when using React Fiber, we must focus on the implementation of these two functions.

Asynchronous rendering component design

Since the reconciliation phase will be interrupted, these life cycle functions before commit may be executed multiple times. React officials have now marked componentWillMount, componentWillReceiveProps and componetWillUpdate as unsafe, and replaced them with the new life cycle functions getDerivedStateFromProps and getSnapshotBeforeUpdate.

static getDerivedStateFromProps

This function is triggered when a component is instantiated or receives new props. It returns an object that can be used to update state. This function can be used together with componentDidMount to replace all the functions of componentWillReceiveProps.

getSnapshotBeforeUpdate

This function is triggered before any changes (such as DOM updates) occur. The return value is passed to componentDidUpdate. This function can be used together with componentDidUpdate to replace all the functions of componentWillUpdate

Let us take a look at the optimized design of components in asynchronous rendering through specific examples.

Get external data

image.png

The previous code uses componentWillMount to obtain external data. In this way, the request may be initiated multiple times during the asynchronous rendering process. It is recommended to put the step of obtaining data in componentDidMount.

add event listener

image.png

This code is an example of subscribing to an external event dispatcher when a component is mounted. It may cause a memory leak, which will cause the asynchronous rendering to be interrupted, and the interruption of the asynchronous rendering will cause the componentWillUnmount to not be executed.

In the design of React, the execution of componentWillMount does not guarantee that componentWillUnmount is executed, but the execution of componentDidMount can guarantee that componentWillUnmount is executed. Therefore, it is recommended that you use the componentDidMount function when adding event listeners.

Update state according to props

image.png

These two pieces of code are examples of updating state based on new props. Because componentWillReceiveProps is often abused to cause errors, the React team will deprecate the componentWillReceiveProps function in the future, and it is recommended to use the new life cycle function getDerivedStateFromProps.

calls external functions

image.png

This is an example of calling an external function when the internal state of a component changes. When we use componetWillUpdate for cascading updates (such as rendering the DOM element first, and then adding a tool according to the size of the DOM element), a state update will cause the callback function to be called multiple times. It is recommended to use componentDidUpdate, because it can ensure that the callback function is only called once each time the status is updated.

Get DOM attributes before update

image.png

This is an example of maintaining the scroll bar position by obtaining DOM attributes. When implementing asynchronous rendering, there will be a time difference between the rendering phase (componentWillUpdate and render) and the submission phase (componentDidUpdate) during the rendering process. During this time, if the user changes the window size, then the scrollHeight obtained in componentWillUpdate is actually It has expired.

In response to this problem, the React team recommends using the new life cycle function getSnapshotBeforeUpdate, which can be called immediately before the change (before the DOM update), passing new parameters to componentDidUpdate, and componentDidUpdate is called again after the change.


记得要微笑
1.9k 声望4.5k 粉丝

知不足而奋进,望远山而前行,卯足劲,不减热爱。