In depth analysis of React Diff principle
Before understanding Diff, let's take a look at the structure of React's virtual DOM
This is the html structure
This is the js code when React renders html. You can try it on babel
It can be seen that this is a tree structure
React will create a tree (pre for short) when calling the render method, and will return a different tree (cur for short) the next time it calls the render method.
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 cur tree.
In order to update the UI efficiently, React proposes a set of O(n) heuristic algorithms 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 which sub elements can be saved under different rendering;
1. Diffing algorithm
1) Layer by layer comparison
When comparing two trees, React compares them layer by layer and only compares DOM nodes in the same color box
First, compare the root nodes of the two trees. Different types of root nodes have different shapes. When the root node is a different type of element, React will tear down the original tree and build a new tree. For example, when an element changes from < a > to < img >, from < article > to < comment >, or from < button > to < div >, a complete reconstruction process will be triggered.
React will destroy the App components (all the sub components of the component will also be destroyed) and recreate a new App component (including the sub components of the App).
The following DOM structure transformation:
React simply considers the position transformation of nodes in the same layer, and only creates and deletes nodes in different layers. When the root node finds that A in the child node is missing, it will directly destroy A; When D finds that it has A child node A, it will create A new A as the child node. Therefore, the actual operation of this structural transformation is:
Although it seems that this algorithm is somewhat "crude", it is based on the first assumption: two different types of elements will produce different trees. According to the official document of React, this assumption has not caused serious performance problems so far. Of course, this also gives us a hint. When implementing our own components, maintaining a stable DOM structure will help to improve performance. For example, sometimes we can hide or show some nodes through CSS instead of actually removing or adding DOM nodes.
2) . compare component elements of the same type
When a component is updated, the component instance will remain unchanged, but the change of data in state or props will call render to change the child elements in the component for update.
3) Compare elements of the same type
When comparing two React elements of the same type, React will retain the DOM node and only compare and update the changed attributes.
React changes the div's className from before to after (similar to the merge operation of updating state in react)
4) . recursion of child nodes
By default (layer by layer comparison), when recursing the child elements of DOM nodes, React will traverse the list of two child elements at the same time; when there is a difference, a mutation is generated.
Therefore, when adding an element at the end of the list, the update cost is relatively small. For example:
React will first match the two trees corresponding to < li > first < / Li >, then match the tree corresponding to the second element < li > second < / Li >, and finally insert the < li > third < / Li > tree of the third element.
If you simply insert the new element into the header, the update cost will be large. For example:
React does not realize that < li > Duke < / Li > and < li > Villanova < / Li > should be retained, but will reconstruct each child element. This situation can cause performance problems.
5),Keys
In order to solve the above problems, React introduces the key attribute. When the child element has a key, React uses the key to match the child elements on the original tree and the child elements on the latest tree. The following example improves the conversion efficiency of the tree after adding a key:
Now React knows that only elements with '2014' keys are new elements, and elements with '2015' and '2016' keys only move. Therefore, only the element with key=2014 is created, and the remaining two elements will not be created.
Therefore, it is better not to use the subscript of the array for the value of the key, because the order of the array may change. It is better to use the unique identifier (id or other attributes) carried by its own data.
2. Verify the Diffing algorithm with an example
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>verification diff algorithm</title> </head> <body> <!-- Prepare a "container" --> <div id="test"></div> <!-- introduce react Core library --> <script type="text/javascript" src="../js/17.0.1/react.development.js"></script> <!-- introduce react-dom,Used to support react operation DOM --> <script type="text/javascript" src="../js/17.0.1/react-dom.development.js"></script> <!-- introduce babel,Used to jsx Turn into js --> <script type="text/javascript" src="../js/17.0.1/babel.min.js"></script> <script type="text/babel"> class Time extends React.Component { state = {date: new Date()} componentDidMount () { setInterval(() => { this.setState({ date: new Date() }) }, 1000) } render () { return ( <div> <h1>hello</h1> <input type="text"/> <span> Now:{this.state.date.toTimeString()} <input type="text"/> </span> </div> ) } } ReactDOM.render(<Time/>,document.getElementById('test')) </script> </body> </html>
Analyze it
The time is being updated, but the data in the input is not lost, indicating that the input is not updated and the real DOM is still used
The time is updating, but the data in the input tag in span is not lost. The diffing algorithm does not only compare one layer, but also the tags in span tag. Therefore, the input data is not lost, and it is still the real DOM before.
3. Function of key (internal principle of key)
-
Role of key in virtual DOM:
1). To put it simply: key is the identifier of the virtual DOM object and plays an extremely important role in updating the display.
2). In detail: when the data in the status changes, react will generate a new virtual DOM according to the new data, and then react will compare the diff of the new virtual DOM with the old virtual dom. The comparison rules are as follows:
a. The same key as the new virtual DOM was found in the old virtual DOM:
(1). If the content in the virtual DOM does not change, use the previous real DOM directly
(2). If the content in the virtual DOM changes, a new real DOM is generated, and then the previous real DOM in the page is replacedb. The same key as the new virtual DOM was not found in the old virtual dom
Create a new real DOM from the data and then render it to the page -
Possible problems caused by using index as key:
1) . if you add or delete data in reverse order:
Unnecessary real DOM updates will be generated = = > the interface effect is no problem, but the efficiency is low.2) . if the structure also contains the DOM of the input class:
An error will be generated. DOM update = = > there is a problem with the interface.3) , attention! If there are no destructive operations such as adding or deleting data in reverse order, it is only used to render the list for display. There is no problem using index as the key.
-
How to select a key in development?
1). It is better to use the unique identifier of each data as key, such as id, mobile phone number, id card number, student id number and so on.
2). If you are sure that it is just a simple display of data, you can also use index.
Let's look at the principle with an example
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>key Role of</title> </head> <body> <div id="test"></div> <!-- introduce react Core library --> <script type="text/javascript" src="../js/react.development.js"></script> <!-- introduce react-dom --> <script type="text/javascript" src="../js/react-dom.development.js"></script> <!-- introduce babel --> <script type="text/javascript" src="../js/babel.min.js"></script> <script type="text/babel"> class Person extends React.Component{ state = { persons:[ {id:1,name:'Xiao Zhang',age:18}, {id:2,name:'petty thief',age:19}, ] } add = ()=>{ const {persons} = this.state const p = {id:persons.length+1,name:'Xiao Wang',age:20} this.setState({persons:[p,...persons]}) } render(){ return ( <div> <h2>Display personnel information</h2> <button onClick={this.add}>Add a Xiao Wang</button> <h3>use index(Index value) as key</h3> <ul> { this.state.persons.map((personObj,index)=>{ return <li key={index}>{personObj.name}---{personObj.age}</li> }) } </ul> <hr/> <hr/> <h3>use id(Unique identification of the data) as key</h3> <ul> { this.state.persons.map((personObj)=>{ return <li key={personObj.id}>{personObj.name}---{personObj.age}</li> }) } </ul> </div> ) } } ReactDOM.render(<Person/>,document.getElementById('test')) </script> </body> </html>
Analyze it
Low efficiency
Solution: use id as key
Only one node is updated, which is efficient
Add an input box to li
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>key Role of</title> </head> <body> <div id="test"></div> <!-- introduce react Core library --> <script type="text/javascript" src="../js/react.development.js"></script> <!-- introduce react-dom --> <script type="text/javascript" src="../js/react-dom.development.js"></script> <!-- introduce babel --> <script type="text/javascript" src="../js/babel.min.js"></script> <script type="text/babel"> class Person extends React.Component{ state = { persons:[ {id:1,name:'Xiao Zhang',age:18}, {id:2,name:'petty thief',age:19}, ] } add = ()=>{ const {persons} = this.state const p = {id:persons.length+1,name:'Xiao Wang',age:20} this.setState({persons:[p,...persons]}) } render(){ return ( <div> <h2>Display personnel information</h2> <button onClick={this.add}>Add a Xiao Wang</button> <h3>use index(Index value) as key</h3> <ul> { this.state.persons.map((personObj,index)=>{ return <li key={index}>{personObj.name}---{personObj.age}<input type="text"/></li> }) } </ul> <hr/> <hr/> <h3>use id(Unique identification of the data) as key</h3> <ul> { this.state.persons.map((personObj)=>{ return <li key={personObj.id}>{personObj.name}---{personObj.age}<input type="text"/></li> }) } </ul> </div> ) } } ReactDOM.render(<Person/>,document.getElementById('test')) </script> </body> </html>
That's it
That's it
That's it
reference resources https://www.jb51.net/article/210302.htm#_label0
as well as https://blog.csdn.net/qq_14993591/article/details/121687727