Author: Lee Hao

If you want to learn about React Fiber systematically, I highly recommend Lin Clark’s YouTube video, which is the best source to understand React Fiber.

www.youtube.com/watch?v=ZCu…

This article will try to describe Fiber in a different style and express this complex system in the simplest and most direct language. However, before reading this article, I want you to have a basic understanding of the React 15 diff algorithm and the virtual DOM.

Why learn Fiber

As a front end engineer, you have plenty of incentives to learn Fiber from both a utilitarian and a non-utilitarian perspective. From a utilitarian point of view:

  1. Fiber is frequently viewed by senior front end engineer positions
  2. Understanding Fiber is one of the most important indicators that Internet companies use to see if programmers are willing to understand something in depth

From a non-utilitarian perspective: Fiber helps you systematize three key aspects of your front-end role:

  1. Browser rendering mechanism
  2. Js runs on a single thread
  3. Front-end performance optimization

Why Fiber

In short, Fiber is used to optimize React performance. First, let’s take a look at act15 (no Fiber) versus Act16 (Fiber).

React 15

React 16

You can see how React 16 has a crushing fluency advantage with Fiber.

How is Fiber optimized for performance

Stack Reconciler’s challenges

Js is a single-threaded language that runs on the main thread of the browser render process. But the main thread is not only a service for js, it is also responsible for other things, such as a response to user input, and the rendering of the page, a person do three things, every two things cannot be parallel, this creates a problem, if say any thing to grab the main line is more than the practical, can only wait for the other two things. In other words, if you have a computation-intensive (CPU intensive) JS task dominating the main thread, the browser will not have time to respond to user input and render the page, and this will cause your page to perform poorly. Unfortunately for React 15, when you use React and have a very large virtual DOM tree, your diff algorithm is just such a continuous, uninterrupted, computationally intensive JS task.

Fiber’s role

Fiber is like adding an operating system to React. Tell it when to diff, when to render, and when to respond to user input. This is analogous to the operating system’s time-slice rotation, much like generator allowing interrupts.

Thanks to Fiber, React has time in every frame to respond to user input and render the page, so it doesn’t look stuck.

The whole process of Reconciler in Fiber

Knowledge of bedding

A frame in the browser

This article won’t go into all the details of how browsers work, but some background is essential to understanding Fiber. The Main Thread of the Renderer Process has many steps in a frame. The Main Thread parses the HTML into a DOM tree, calculates and inserts styles into the DOM tree, calculates the layout (where each DOM node is on the screen), Generate the operating system level draw commands, generate the order for each layer to draw and generate the sequence of draw commands, and then perform layer composition (note that this step is not necessarily done by the main thread, but may be done by the composition frame thread). If the main thread has extra time in a frame, then it will execute the callback function in requestIdleCallback.

requestIdleCallback

The requestIdleCallback API is a very important concept in Fiber. We already know that the main thread of the browser renderer process is responsible for many things, all of which is done by the main thread in the image below. If the main thread has time, it will perform tasks in the requestIdleCallback. If there is no time, the callback inside the requestIdleCallback is deferred to the next frame, and if there is no time for the next frame, it continues to be pushed back.

We can do a comparison experiment with requestAmimationFrame. The purpose of the experiment is to verify that rIC is not executed every frame.

        num = 300
        function f() {
            console.log('print')
            num -= 1
            if (num > 0)
                requestIdleCallback(f) // Try requestAmimationFrame instead
        }
        f()
Copy the code

You will find that for rAF, the program will stop after 5s, but for rIC, the program will run for much longer than 5s.

React will use the requestIdleCallback to update Fiber nodes one by one. If the browser doesn’t have time for the requestIdleCallback, the update process will be paused to make way for the main thread.

Update Process Overview

From diff to updating the real DOM, the process can be broken down into four steps.

  1. Generate UpdateQueue
  2. Update the WIP Tree
  3. Generate EffectList
  4. Updating the real DOM

The first three phases are collectively called the Render phase, and the last phase is called the Commit phase. One thing you can think about is, for Fiber algorithm, which phase can use requestIdleCallback, i.e. can be interrupted?

Detailed update process

code

import React, { Component, PureComponent } from 'react';
class List extends Component {
    constructor(props) {
        super(props);
        this.state = { 
            list: [1.2.3]}}render() { 
        const {list}=this.state
        return ( <div>
            <button onClick={()= >{ let {list}=this.state for(let i=0; i<list.length; i++){ list[i]*=list[i] } this.setState({ list }) }}>^2</button>
            <Item num={list[0]} key={0} />
            <Item num={list[1]} key={1} />
            <Item num={list[2]} key={2} />
        </div> );
    }
}
export default List;
Copy the code

class Item extends PureComponent {
    constructor(props) {
        super(props);
    }
    render() { 
        return (<div>
            {this.props.num}
        </div>); }}export default Item;
Copy the code

Code Overview:

The parent component

  1. Component name: List
  2. The state:List = [1, 2, 3]
  3. Props: no

Child components:

  1. Component name: Item
  2. State: no
  3. Props:num = this.state.list[i]

SetState of the List component

React will inject setState into update ue as some kind of data structure

When we click the button to trigger setState, [1,2,3] is multiplied by itself to become [1,4,9].

Fiber tree

When the List component is first rendered, react generates a Fiber tree in memory using JSX. What does the tree look like? It looks like this:

Those of you who have seen React 15 know that this data structure is not fundamentally different from the virtual DOM tree. The most important difference is that it has more Pointers, but it is still essentially a tree structure. This gives us the first piece of information about the Fiber structure: Fiber is a linked list tree.

Each node in Fiber has Pointers to its children, parents, and siblings.

Why a linked list tree

Recursion is bad for protecting the scene. In fact, the React team made some attempts with the ES6 Generator, which failed. It turns out that the linked list is the best data structure for interrupt recovery, because the linked list is easy to stop and protect the scene in the loop, allowing the main thread to respond to user input and do page rendering.

The Work In Progress

React generates the Work in Progress tree based on the Current tree and update queue. You can simply think of current Tree and Work in Progress Tree as snapshots of the virtual DOM before and after setState.

React updates from the component that triggers setState. Since the List’s state is changed, the effectTag of the List is set to Update, and the effectList generates a linked List node (I’ll use arrays for convenience). React actually uses linked lists.)

[{
    instance: List
    type: "update"property: "the state list 'value: [1.4.9]}]Copy the code

The button is an element in the List, but it does not have any attribute updates, and the WIP Tree copies the node directly from the Current Tree.

The first Item is copied directly from the current tree because props. Num is not changed. The logic in shouldComponentUpdate should not trigger the update.

Since the first Item component does not need to trigger an update, its children can be copied entirely from the Current Tree. If the rIC time slice does not expire, React continues to update FiberNode. React finds that the parent element of the second Item is changed, that is, the props of the Item is changed, and the innerText property of the div element inside the Item is changed. So the FiberNode effectList for the second Item becomes

 [{
    instance: Item,	
    type: ‘update’,
    property: 'props. Num'value: 4
}]
Copy the code

The effectList corresponding to div’s FiberNode becomes

[{
   name: div,	
   type: ‘update’,
   property: 'the innerText'value: 4
}]
Copy the code

RequestIdleCallback gives up the main thread

If the requestIdleCallback time slice expires at this point, react hands control to the browser, which then renders the page in response to user input.

In the next frame, React again updates the third Item after rIC takes control. After checking the parent element’s state, it learns that its props are updated and the Item’s effectList becomes

[{
    instance: Item, 
    type: ‘update’,
    property: 'props. Num'value: 9
}]
Copy the code

Notice that the innerText of its child div has changed, so that the div’s effectList becomes

[{
    instance: div, 
    type: ‘update’,
    property: 'the innerText'value: 9
}]
Copy the code

How to concatenate the Effect List

The effectList of the child node is concatenated to the head of the parent node on return, and the sibling node concatenates the effectList of the first sibling node as the head in sequence. In this case,

  1. The second div comes back, spells on the second Item,
  2. div -> Item (1)
  3. The third div returns, spelt to the third Item
  4. div -> Item (2)
  5. Spliced with PI (2)
  6. div -> Item -> div -> Item
  7. Returning to the List
  8. div -> Item -> div -> Item -> List

In fact, as mentioned in the previous section, React collects all effectLists marked with effectTag elements, but to catch the main contradiction, I focused on the effectLists of two divs

[[
    instance: div, 
    type: ‘update’,
    property: 'the innerText'value: 4
},
{
    instance: div, 
    type: ‘update’,
    property: 'the innerText'value: 9
}]
Copy the code

React updates the real DOM in batches with effectLists. This phase is called the COMMIT phase. It calls the method that updates the real DOM all at once, in this case, dom.innertext = ‘XXX’

The answer to the render versus Commit debate

At this point, I think some of you can guess who can be interrupted in the Render phase and commit phase. The answer is obvious, the Render phase can be interrupted, the Commit phase cannot be interrupted. Please note that render is not the same as the render inside react. Commit is the stage to update the real DOM. If the COMMIT is interrupted, this means that updates to the DOM are not one-off, so 1, 2, and 3 May be updated to 1, 4, and 3 without interruption. This is clearly unacceptable to the user.

Utah on React Fiber

Although React Fiber is considered by many people abroad to be a high-tech design, Fiber has received mixed reviews. Vue Conf of Yuxi states:

If we can make updates fast enough, time sharding is theoretically unnecessary. React Fiber is essentially a solution to React update inefficiencies. Don’t expect Fiber to improve your existing applications substantially. If performance issues are your own problem, you’ll have to do it yourself.

The resources

  • 【1】Lin Clark – A Cartoon Intro to Fiber-React Conf 2017 www.youtube.com/watch?v=ZCu…
  • 【2】React Fiber Architecture github.com/acdlite/rea…
  • 【 3 】 Facebook announces the React Fiber, a rewrite of the its React framework techcrunch.com/2017/04/18/…
  • [4] This is probably the most popular way to open React Fiber juejin.cn/post/684490…
  • [5] the React principle of Fiber is introduced segmentfault.com/a/119000001…