Writing in the front

There are many articles about react source code interpretation, many of which are simply posted source code, listing variable names. React react React React react react react react

For those who are new to the source code or want to understand the React implementation, the stack of function variables is a deterrent.

Therefore, we plan to start a series of articles to replace the source code with simple code snippet and dismantle the core modules of React time sharding, priority scheduling, diff and so on, so that we can understand the principle at a glance.

Why did Act15 need to evolve

Sin # 1: Synchronous rendering blocks the main thread

In Act15, the entire process is synchronized from setState to DOM node rendering to the page, so if one of these sections takes too long, the main thread will block.

Since JS execution is single threaded, the JS thread is mutually exclusive with other threads of the browser. If the JS thread blocks, the browser’s rendering thread and event thread will also be suspended accordingly. In this case, the browser’s native events triggered by the user also do not respond, causing a delay.

Question: When does react15 cause blocking?

React15 uses a virtual DOM tree with a tree structure and uses recursion for node traversal. Recursion means that the construction of the virtual DOM tree is a synchronous process that cannot be interrupted once it is started. And the deeper the DOM node hierarchy, the more nodes there are, the longer the DIff process hogs the JS thread.

Of course, the Internet is so said, in fact is not really a tree structure, is not really a recursive way of node traversal, or need to go through the actual source research, so I looked at the source of [email protected]

Proof 1: Tree structure

<div key={'outermost node'}>
  {
    ['a'.'b'.'c'.'d'].map( (v,index) = >
      <div key={'first layer child node-The ${v} `} >
        <span key={` ${v} child node '}>ParentNode: {n}</span>
      </div>
    )
  }
</div>
Copy the code

The JSX code above iterates through the tree structure as it transforms into a DOM tree

Claim 2: Recursive traversal

Here the use of pseudo-code in the form of simulated react15 node traversal, specific source code call span large paste code is not good analysis, interested students can look at the real source code to view the specific details

 functionBuild nodes (node) {
    if(Has child nodes)returnGenerate child nodes (nodes)returnNode}functionGenerate child nodes (children) {
    constList of children = [] children.map(child= >Push (build node (child))}returnList of child nodes}functionMount a node (node) {
    container.insertBefore(node)
  }
  function Render(Components, containers,) {
    constApplication root node = component ()constNode tree = Build node (application root node) Mount node (node tree, container)}function Count(params) {
    return <div>1<div>
  }
  Render(<Count/>.document.querySelector('#root'))
Copy the code

As you can see, when traversing a node and finding a child node below it, it recursively calls the node building method to continue building the DOM tree, and the whole DOM tree building process is synchronous.

Sin # 2: Unable to merge setState in asynchronous functions

In addition to blocking, the merge update mechanism of setState under Act15 is to merge setState executed synchronously within a function on a function basis. The problem is that the setState in an asynchronous function cannot be merged.

  • Problem 1: In asynchronous functionssetStateUpdates are presented synchronously
  • Problem 2: Each within an asynchronous functionsetStateBoth trigger a full view update, causing a performance penalty

Here is the problem code

state = { count: 0 }
setCount() {
    this.setState({ count: 1 })
    console.log(this.state.count) // Print 0, this is normal, state will not be updated immediately
    setTimeout(() = > {
        this.setState({ count: 2 })
        console.log(this.state.count) // Output 2, state updated synchronously, not merged})}Copy the code

How w does the above code output this result, and how does the react15 merge update implement?

Just so you know, I’ll explain it in a later series of articles, with 30 lines of code showing you how act15 merge updates work

What improvements have been made to React with Fiber

To address the pain points of Act15, react rewrote the entire architecture after version 16+ to implement asynchronous interruptible updates. Asynchronous interruptible update is simple, but how to implement it?

Reviewing the two major pain points of Act15, we need to address two things

  1. Solve blocking problems.
  2. Enable setState to be merged in asynchronous functions.

These two issues will be addressed in the following sections

Solve blocking problems

Looking at the pseudocode traversed by the Act15 node above, it’s not hard to see that there are two root causes of the blocking

  1. Recursively traverses the node tree, cannot interrupt traversal
  2. Traversing the node tree will occupy the main thread, blocking other threads in the browser

Solution 1: Change the tree structure and node traversal mode

React15 uses a tree structure to connect the whole tree, which indirectly leads to react15 traversing the virtual DOM tree layer by layer in the way of recursion + child node for loop, and the process cannot be interrupted.

To implement interruptible traversal, instead of recursion, use while traversal to satisfy the interrupt requirement

But tree structure is not convenient to do while traversal, nesting level is deep, many branches, so what to do?

By flattening the entire tree and describing the tree structure as a linked list, I can easily traverse nodes one by one without having to maintain redundant variable records, and it’s much clearer to do traversal interrupts through a while loop

Let me simply simulate the traverse of act16+ in pseudocode form

letLucky nodes that need to be traversed =null
functionBuild nodes () {
    / * * *... This is where the node building work is done */Lucky node to be traversed = lucky node to be traversed. Next}functionNode traversal () {
    whileLucky nodes that need to be traversed! =null) {build node ()}}functionScheduling () {Lucky nodes that need to be traversed = React root node traversal ()} schedule ()Copy the code

Note that lucky nodes that need to be traversed = lucky nodes that need to be traversed. Next, React does not simply use next to describe node relationships, which I will describe in detail in a later series of articles

Solution 2: Time sharding

Now that we’ve finally implemented interruptible updates, we’re halfway through React16, but we still need one asynchronous. How do WE do that? That’s time sharding

Time sharding, as the name suggests, is the setting of a fixed, continuous interval of time with intervals.

What is fixed? I work eight hours a day on a regular basis

What is continuous? I need to go to work every day

What is spacing? The weekend to rest

The react time sharding corresponds to

  1. Time fragmentation fixed around 5 ms (will fluctuate according to the priority, survival desire)
  2. Sharding governs the interrupt and start of react work (actually only part of the work)
  3. There is an interval between shards, which gives the browser free time to work on other threads

The following is a simple implementation of time sharding

I'll leave it to the next chapter, because IT's too much to digestCopy the code

Intuitive representation of time sharding in performance (basically controlled around 5 milliseconds)

Enable setState to be merged in asynchronous functions

The implementation of REact16 + is based on the design of the whole Fiber architecture, which requires some pre-knowledge or better understanding of time sharding, asynchronous scheduling, Lane priority mechanism, state calculation method and event system

So let me outline the implementation here

  1. SetState is executed every time

    A. Associate the priority of the update to the current Fiber node and the root Fiber node

    B. Run the scheduling function

  2. The scheduling function first makes a logical determination to determine whether the priority of the current application root node is equal to the priority of the currently scheduled node

    A. the same. Is the setState under the same function, can merge updates, do not repeatedly initiate coordination tasks

    [B]. Unequal. Initiate coordination task

There are two kinds of unequal situations: one is the first time to initiate scheduling, and the other is the high-priority task.

If you have a certain understanding of the source code partners may have a little understand what I am saying here, the above is not completely corresponding to the source code, but probably the logic is the same, I will explain the priority scheduling in more detail.

Learn about react’s new architecture from a macro perspective

The first part of the series is mainly for you to understand react16+ source code to do a pre-knowledge of the foundation, so that you have a general understanding of the composition of react16+, the following is a react16+ module function distribution map

Scheduler

Scheduler is mainly responsible for react task scheduling, including fragment scheduling and priority scheduling

  • The main task of sharding scheduling is to perform node traversal intermittently in the Reconcile (render) phase

  • Priority scheduling is used to divide react tasks into multiple priority types so that high-priority tasks can respond quickly

Reconciler

Reconciler is primarily responsible for the construction of Fiber nodes and the creation of corresponding side effects

  • After the introduction of the priority mechanism, the state calculation does not simply overwrite the state calculation, which is associated with the restart logic of low-priority tasks

  • Diff is done by walking through old and new Fiber trees to find nodes that need to be added or deleted

  • Add, delete, and delete nodes to the Flags attribute of the Fiber node in the form of bits and operations, and wait for the COMMIT phase to clear these side effects (including but not limited to node additions, deletions, useEffect execution, ref updates, etc.).

Renderer

All the Renderer (COMMIT) phase does is clean up the side effects and start the next round of scheduling

This is the basic structure of React and the responsibilities of each module. To facilitate subsequent interpretation, I will use the Render stage for Reconciler and the COMMIT stage for Renderer

Write in the last

This article mainly describes the evolution of REACT and the basic structure of the new REACT architecture. In the next chapter, I will talk about time sharding in React and simulate the running process of time sharding in combination with react tasks.

If there is something wrong, I hope you can correct it. If you don’t understand, you can put forward questions and I will answer them one by one. Every communication process is a collision of ideas and learning, and you can feel free to diss.

Finally, I would like to thank My react teacher for his react technology Revelations, which helped me a lot to read the source code.