React Diff Principle In-depth Analysis

React Diff Principle In-depth Analysis

Before understanding Diff, let's take a look at the structure of React's virtual DOM

This is the html structure

<div id="father">
  <p class="child">I am child p</p>
  <div class="child">I am child div</div>
</div>

This is the js code when React renders HTML. You can try it on babel

React.createElement("div", {id: "father"}, 
    React.createElement("p", {class: "child"}, "I am child p"),             
    React.createElement("div", {class: "child"}, "I am child div")
);

This shows that this is a tree structure.

React creates a tree (referred to as pre) when calling the render method, and returns a different tree (referred to as cur) the next time the render method is called. React will compare the differences between the pre and cur trees to determine how to update the UI efficiently and ensure that the current UI is synchronized with the latest tree cur.

In order to update the UI efficiently, React proposes an O(n) heuristic algorithm based on the following two assumptions:

1. Two different types of elements will produce different trees;

2. Developers can set the key attribute to tell the renderer which sub-elements can be kept unchanged under different renderings;

Diffing Algorithm

Layer-by-layer comparison

When comparing two trees, React compares them layer by layer and only compares DOM nodes within the same color box.

First, compare the root nodes of the two trees. Different types of root nodes will have different shapes. When the root node is an element of a different type, React will tear down the original tree and build a new one. For example, when an element changes from <a> to <img>, from <Article> to <Comment>, or from <Button> to <div>, a complete rebuild process is triggered.

//before
<div>
    <App/>
</div>
//after
<p>
    <App/>
</p>

React will destroy the App component (all its subcomponents are also destroyed) and recreate a new App component (including its subcomponents).

The following DOM structure conversion:

React will only simply consider the position changes of nodes in the same layer. For nodes in different layers, there are only simple creation and deletion. When the root node finds that A is missing from its child node, it will destroy A directly; and when D finds that it has an extra child node A, it will create a new A as its child node. Therefore, the actual operation for this structural transformation is:

A.destroy();
A = new A();
A.append(new B());
A.append(new C());
D.append(A);

Although this algorithm seems a bit "crude", it is based on the first assumption: two different types of elements will produce different trees. According to the React official documentation, this assumption has not caused serious performance issues so far. This of course also gives us a hint that maintaining a stable DOM structure will help improve performance when implementing our own components. For example, we can sometimes hide or show certain nodes through CSS instead of actually removing or adding DOM nodes.

Compare components of the same type

When a component is updated, the component instance remains unchanged, but changes in the data in state or props will call render to change the child elements in the component for update.

Comparing elements of the same type

When comparing two React elements of the same type, React will preserve the DOM nodes and only compare and update the properties that have changed.

<div className="before" title="stuff" />

<div className="after" title="stuff" />

React changes the className of div from before to after (similar to the merge operation of updating state in React).

Recursively on child nodes

By default (level-by-level comparison), when recursing over the children of a DOM node, React will traverse both lists of children at the same time; when a difference is found, a mutation is generated.

Therefore, when adding elements to the end of the list, the update overhead is relatively small. For example:

<ul>
  <li>first</li>
  <li>second</li>
</ul>

<ul>
  <li>first</li>
  <li>second</li>
  <li>third</li>
</ul>

React will first match the two <li>first</li> trees, then match the tree for the second element <li>second</li> , and finally insert the third element's <li>third</li> tree.

If you simply insert the new elements into the table header, the update overhead will be relatively large. for example:

<ul>
  <li>Duke</li>
  <li>Villanova</li>
</ul>

<ul>
  <li>Connecticut</li>
  <li>Duke</li>
  <li>Villanova</li>
</ul>

React won't realize that it should keep <li>Duke</li> and <li>Villanova</li> and will rebuild each child. This situation can cause performance problems.

Keys

To solve the above problems, React introduced the key attribute. When a child has a key, React uses the key to match the child in the old tree with the child in the newest tree. The following example improves the efficiency of tree conversion after adding a key:

<ul>
  <li key="2015">Duke</li>
  <li key="2016">Villanova</li>
</ul>

<ul>
  <li key="2014">Connecticut</li>
  <li key="2015">Duke</li>
  <li key="2016">Villanova</li>
</ul>

Now React knows that only the element with the '2014' key is new, and the elements with the '2015' and '2016' keys have simply moved. Therefore, only the element with key=2014 is created, and the remaining two elements are not created.

Therefore, it is best not to use the array subscript as the key value, because the order of the array may change. It is best to use the unique identifier (id or other attributes) carried by the data itself.

1. The role of key in virtual DOM:
1). Simply put: key is the identifier of the virtual DOM object, and key plays an extremely important role when updating the display.

2). In detail: When the data in the state changes, React will generate a new virtual DOM based on the new data, and then React will perform a diff comparison between the new virtual DOM and the old virtual DOM. The comparison rules are as follows:

a. The same key as the new virtual DOM is found in the old virtual DOM:
(1) If the content in the virtual DOM has not changed, directly use the previous real DOM
(2) If the content in the virtual DOM changes, a new real DOM is generated and then replaces the previous real DOM in the page.

b. The same key as the new virtual DOM is not found in the old virtual DOM
Create a new real DOM based on the data and then render it to the page

2. Problems that may arise when using index as key:
1. If you perform operations that destroy the order of data, such as adding or deleting data in reverse order:
Unnecessary real DOM updates will be generated ==> The interface effect is fine, but the efficiency is low.

2. If the structure also contains DOM of input class:
Will produce wrong DOM update ==> There is something wrong with the interface.
3. Attention! If there is no order-destroying operation such as adding or deleting data in reverse order,
It is only used to render the list for display, so there is no problem using index as the key.

The above is the detailed content of the in-depth analysis of the React Diff principle. For more information about the React Diff principle, please pay attention to other related articles on 123WORDPRESS.COM!

You may also be interested in:
  • Detailed explanation of virtual DOM and diff algorithm in react
  • React diff algorithm source code analysis
  • Detailed explanation of DOM DIFF algorithm in react application
  • A brief discussion on analyzing the Diff algorithm from the React rendering process
  • Example implementation of React diff algorithm

<<:  Solution to Ubuntu 18.04 not being able to connect to the network in VMware virtual machine

>>:  Introduction to MySQL statement comments

Recommend

A detailed introduction to setting up Jenkins on Tencent Cloud Server

Table of contents 1. Connect to Tencent Cloud Ser...

MySQL Order By Multi-Field Sorting Rules Code Example

Say it in advance On a whim, I want to know what ...

What is the file mysql-bin.000001 in mysql? Can it be deleted?

After installing MySQL using ports, I found that ...

Native JS to achieve drag photo wall

This article shares with you a draggable photo wa...

Advantages of MySQL covering indexes

A common suggestion is to create indexes for WHER...

The use of v-model in vue3 components and in-depth explanation

Table of contents Use two-way binding data in v-m...

Vue+Router+Element to implement a simple navigation bar

This project shares the specific code of Vue+Rout...

Three notification bar scrolling effects implemented with pure CSS

Preface The notification bar component is a relat...

Quickly learn MySQL basics

Table of contents Understanding SQL Understanding...

Detailed explanation of the difference between var, let and const in JavaScript

Table of contents As a global variable Variable H...

Sample code using vue-router in html

Introducing vue and vue-router <script src=&qu...

A brief discussion on CSS blocking merging and other effects

Non-orthogonal margins When margin is used, it wi...

Analysis of the principle and usage of MySQL continuous aggregation

This article uses examples to illustrate the prin...