There are three articles in this series:

Quick question before class:

  1. How is JSX implemented
  2. Why can’t I write a for loop in JSX
  3. Why do we need to introduce the React library at the top of the React file
  4. Why do user-defined components start with uppercase
  5. Why is class written as className?
  6. What is a Virtual DOM
  7. What is the Diff algorithm
  8. Why do dynamically generated lists need keys
  9. Why not do side effects in componentWillMount
  10. How is the class Component lifecycle implemented

The core idea of React

  1. declarative
  2. componentization
  3. Learn to write anywhere at once

What means are needed to achieve this

  1. Manipulating views through templates (JSX)
  2. How do I insert a template into a real DOM that requires a virtual DOM for performance
  3. How to schedule updates in two ways: Stack Reconciler and Fiber Reconciler
  4. Learning to write anywhere once requires unpacking each part of the package

How is JSX implemented

  1. Why need jsx:zh-hans.reactjs.org/docs/introd…
  2. How to write JS and HTML together: because only JS is a Turing-complete language, do you use JS to describe HTML

Question: If we have the following HTML structure, how do we describe it in JS?

<div class="parent">
  <span>child1</span>
  <span>child2</span>
</div>
Copy the code

We get a JS object that can be described as such

const element = {
  type: 'div'.props: {className: 'parent'},
  children: [{type: 'span'.props: null.children: ['child1'] {},type: 'span'.props: null.children: ['child2']]}}Copy the code
  1. Obviously, it’s too cumbersome to write like this, so React uses JSX syntax extensions
  2. JSX => reactElement is implemented through Babel (babel-preset-react)

Online experience link: babeljs. IO /repl

This section addresses the problem

  • How is JSX implemented
  • Why can’t I write a for loop in JSX
  • Why do we need to introduce the React library at the top of the React file
  • Why do user-defined components start with uppercase
  • Why is class written as className?

What is a virtual DOM

  1. Conclusion: JS objects simulate DOM trees
  2. Why you need a virtual DOM:
  • Because manipulating the DOM is expensive and can cause performance problems (browser rearranges and redraws) [illustrated here with DevTool]
  • Virtual DOM can be combined with diff algorithm to achieve as few DOM operations as possible
  • Create cross-platform applications


This section addresses the problem

  • What is a Virtual DOM

The Diff algorithm

  1. The Diff algorithm is to find the difference between the old and new virtual DOM trees (similar to Git DIf)
  2. If it takes n ^ 2 time just to compare two trees plus update it becomes n ^ 3

  1. The react diff algorithm

  • Compare different types of elements
  • Compare elements of the same type
  • Compare component elements of the same type
  • Recurse the child nodes
  • keys
  1. Premise:
  • Two identical components produce similar DOM structures, and different components produce different DOM structures
  • For a set of child nodes at the same level, they can be distinguished by unique IDS.

This section addresses the problem

  • What is the Diff algorithm
  • Why do dynamically generated lists need keys

Coordinate (Reconciliation)

Concept: as I understand it, this is the process between updates -> DOM changes. It includes the Diff algorithm. React has two sets of reconciling algorithms: Stack Reconciler ([email protected]) and Fiber Reconciler ([email protected])

Note: The mount phase is also an update in React

stack reconciler

concept

The scheduling algorithm before React 16, known as the Stack Reconciler, is characterized by a top-down update approach, such as the process that follows a call to this.setState() that looks like this: This.setstate () => Generate the virtual DOM => Diff algorithm comparison => Find the element to update => put it in the update queue

Think about it: What’s wrong with this implementation?

The problem

If the entire application is large, the execution of JS will occupy the main thread for a long time, and the browser cannot respond to the user’s operation in a timely manner, resulting in the page display lag. Suppose we have a large triangle component that is made up of many sub-components, each of which is constantly changing numbers while the entire triangle is constantly getting wider and narrower. Let’s look at how the two Reconcilers behave:



We can see that rendering in the Stack Reconciler is inferior to the process that comes from the fiber Reconciler because of the synchronous, top-down way that updates are made in the Stack Reconciler, As soon as the update process starts, it will “go black” until all the nodes are matched. This way, if the number of nodes is small, it is good. If the number of nodes is large, let’s say there are 200 nodes, and it takes 1ms for each node to perform diFF algorithm, then it will take 200ms for all the nodes to be matched. In other words, the browser cannot handle other tasks, such as rendering the view, in the 200ms, generally 30 frames or more will make people feel smooth, then each frame will need 33.3ms, obviously 200ms > 33.3ms, so the frame rate will be very low because of the long execution of JS. After 200ms, when the browser renders the missed page all at once, you will feel that the page is not coherent.

We know that in order to achieve a smooth display, the refresh rate (FPS) cannot be too low. The average refresh rate of modern browsers is 60FPS, so each frame has a time of 1000/60 ≈ 16 ms. In this 16 ms, the browser will schedule the following

  • Handle user interactions
  • JS parse execution
  • Start frame. Window size changes, page rolling, etc
  • rAF(requestAnimationFrame)
  • layout
  • draw

You can see that there are a lot of things to do in the browser frame. If the execution of the JS engine takes too long, other tasks (such as responding to user interaction) will have to be delayed, which is the reason for the lag.

Fiber reconciler

concept

The React 16 version is a rewrite of the previous Stack Reconciler, known as the Fiber Reconciler, which is designed to address the problems inherent in the Stack Reconciler, as well as some lingering issues from history. We hope to achieve the following goals:

  • Be able to slice interruptible tasks
  • Ability to reprioritize, reset, and reuse tasks
  • Ability to interleave parent and child elements to support layout in React
  • In therender()Returns multiple elements in
  • Better support for error boundaries

To implement these features is a very important point: slice, spends a large task into many small tasks, every small task is done to see if need to give way to other higher priority tasks, thus to make sure that the threads will not be the only occupied all the time, we put each section (small) is known as a fiber.


When to slice?


  • Phase1: render/reconciliation
  • Phase2: commit

The first stage is responsible for generating the Virtual DOM -> diff algorithm -> generating the update to be executed. The second stage is responsible for rendering the update to the DOM Tree. If we slice in the second stage, the page display may be incoherent, which will affect the user experience. So it makes more sense to slice it in the first stage. What do I get? In computer science, it is called coroutine or fiber. It is a finer granularity task unit than thread tillage. Although JS does not have this mechanism originally (probably), I think React probably wants to use this idea to achieve more detailed control task execution. How do I schedule task execution? React is implemented using two low-level JS apis:

  • RequestIdleCallback This method receives a callback that will be called when the browser is idle

  • RequestAnimationFrame This method receives a callback that will be called before the next browser redraw

The difference between requestIdleCallback and requestAnimationFrame is that requestIdleCallback is executed when the browser is free whereas requestAnimationFrame is executed on every frame, So the higher-priority tasks are given to requestAnimationFrame and the lower-priority tasks are given to requestIdleCallback, but to prevent the lower-priority tasks from being executed because the browser is always occupied, RequestIdleCallback also provides a timeout parameter specifying that the callback is enforced after this event is exceeded.

implementation

Let’s take a look at how the Fiber Reconciler works through an example, but first let’s recognize some nouns in the React source code.

Noun explanation

  • Fiber

Fiber is the smallest unit of work in React. In React, ClassComponent, FunctionComponent, normal DOM nodes, and text nodes all correspond to a Fiber object. The Fiber object is essentially a Javascript object.

  • child

Child is a property on the Fiber object that points to its child node (Fiber).

  • sibling

Sibling is a property on the Fiber object that points to its sibling node (Fiber)

  • return

Return is a property on the Fiber object that points to its parent (Fiber).

  • stateNode

The stateNode is a property on the Fiber object that represents instances of the Fiber object, such as Class instances, DOM nodes, and so on

  • current

Current represents the Fiber object that has been updated

  • workInProgress

WorkInProgress represents the Fiber object being updated

  • alternate

Alternate is used for current or workInProgress, the alternate of current for workInProgress, and the alternate of workInProgress for current

  • FiberRoot

FiberRoot represents the starting point for the entire application, and it holds container information, corresponding Fiber objects (RootFiber), and so on

  • RootFiber (HostRoot)

RootFiber represents the root node of the entire fiber tree. The stateNode inside it points to FiberRoot and its return is NULL

Fiber tree


For example

We have an App component that contains a button and a List component, and when you click the button, the numbers in the List component are squared, and there’s a button outside of the App component that’s not created with React, What it does is it enlarges the font when you click on it.

Mount the stage

During the first render phase, also known as the mount phase, React creates the entire Fiber tree from top to bottom, starting with the FiberRoot, creating the fiber nodes through a loop called a Work loop, which first iterates over the child nodes. When there are no child nodes to traverse the sibling nodes, the goal of creating all nodes is finally achieved. Take our demo as an example, the creation order of fiber is shown as the arrow in the figure below.

The constructed Fiber tree is shown in the following figure

Update the stage

In this case, we generate an update by clicking on the [^2] button, and the resulting update is placed in the List component’s update queue. Asynchronous updates in the Fiber Reconciler are not processed immediately, it executes the schedule. Let the scheduler determine when to let it run, and the scheduler uses the requestIdleCallback mentioned above to determine when to process the update.
























































































The impact on us

The Fiber Reconciler divides an update into two phases: Reconciliation Phase and Commit Phase. The first Phase is the Reconciliation process described above. It is used to find out which DOM needs to be updated, and this Phase can be interrupted. The second phase, which cannot be interrupted, is the process of rendering the found DOM. What affects us is the lifecycle functions called by the two phases, bounded by the render function. The first phase calls the following lifecycle functions:

  • componentWillMount
  • componentWillReceiveProps
  • shouldComponentUpdate
  • componentWillUpdate
  • render

Lifecycle functions called in the second phase:

  • componentDidMount
  • componentDidUpdate
  • componentWillUnmount

Since the Fiber Reconciler can cause the first stage to be executed multiple times, we need to be careful not to perform operations that can only be called once in the first stage’s lifecycle functions.

This section addresses the problem

  • Why not do side effects in componentWillMount
  • How is the class Component lifecycle implemented

The resources

  • Github.com/acdlite/rea…
  • react.jokcy.me/
  • Juejin. Cn/post / 684490…
  • www.youtube.com/watch?v=ZCu…

Github

Includes annotated source code, demos, and flowcharts github.com/kwzm/learn-…