Performance Optimization of React components and react Performance Optimization
Gartner: "We should forget to ignore small performance optimizations. in 97% of the cases, premature optimization is the source of all evil, we should focus on the other 3% of the Code that has the most critical impact on performance. "
Do not waste the effort of Performance Optimization on code that has little improvement on the overall performance. It is not too early to optimize the performance that has a key impact on the performance. Because the most critical impact on performance often involves the core of the solution, which determines the overall architecture and will be more involved in future changes.
1. Performance Optimization of a single React component
React uses Virtual DOM to improve rendering performance. Although every page update is the latest rendering of the most components, it does not discard all the previous rendering content. With the help of Virtual DOM, react can calculate the minimum modification to the DOM tree, which is the secret to React rendering quickly by default;
However, although Virtual DOM can minimize the number of DOM operations each time, it is still a complicated process to calculate and compare Virtual DOM;
Of course, if you can determine that the rendering results will not change before you start to compute Virtual DOM, you can do not perform Virtual DOM calculation and comparison, and the speed will be faster.
2. Default Implementation of shouldComponentUpdate
Since the rendering can be prevented before the component starts to calculate Virtual DOM and the rendering result will not change, so as to improve performance, we naturally want to use shouldComponentUpdate (nextProp, nextState)
The shouldComponentUpdate function is called before the render function and determines "when new rendering is not required ";
That is, a Boolean value is returned, which determines whether the update is ongoing. The default value is true. If the return value is false, the update is interrupted;
shouldComponentUpdate(nextProp,nextState){ return (nextProp.completed !== this.props.completed) || (nextProp.text !== this.props.text)}
NextProps is the props passed in for this update. For this component, the prop that affects the rendering content only has completed and text, as long as the two prop do not change, shouldComponentUpdate can return false to prevent unnecessary updates.
However, the above comparison is only 'shallow comparison '. If the type is basic type, as long as the value is the same, then "Shallow comparison"
The two are also considered the same:
What if the prop type is complex objects?
For complex objects, the 'shallow comparison 'method only determines whether the two prop are referenced by the same object. If not, even if the content of the two objects is exactly the same, they will be considered as different two prop. The "Deep comparison" is used, but the structure of the object is unpredictable. If recursive comparison is performed on each field, it will not only make the code more complex, it may also cause performance problems.
Therefore, to determine whether prop of the object type is the same, you must ensure that prop points to the same JavaScript Object:
<Foo styleProp = {{color: "red"}}>
To avoid using the above input method, the {color: "red"} object should be re-created for each rendering. The reference address is different each time, resulting in different styleProp values.
Const footStyle = {color: "red"}; // make sure this initialization is only executed once. Do not place it in the render function <Foo styleProp = {footStyle}>
Use 'singlemode' to ensure that the imported styleProp points to the same object
What if it is a function?
<Foo onToggle={() => onToggleTodo(item.id)}/>
Avoid using the above function transfer mode, because the value assigned here is an anonymous function, which is generated when the value is assigned. That is to say, a new function is generated every rendering, this is the problem.
What if there are many prop to be passed?
~~ If React-Redux is used, shouldComponentUpdate is implemented by default.
3. Performance Optimization for multiple React Components
When a React component is loaded, updated, and detached, a series of lifecycle functions of the component are called. However, these lifecycle functions are for a specific React component function. In an application, there are many React components combined from top to bottom, and the rendering process between them is more complex.
The rendering process of the same component should also consider three processes: Loading, updating, and detaching.
In the loading phase, components must be completely rendered once, and all the child components from this React component must go through the Loading Life Of The React component.
Cycle, so there is not much optimization to do.
There is only one life cycle function componentWillUnmount in the unload phase. This function only cleans the event processing listening added by componentDidMount, and there is no room for optimization;
4. Reconciliation in the React update phase
During the component update process, Virtual DOM updates are built and compared with the previous Virtual DOM to identify the differences and update with the minimal DOM operations.
Harmonic process: This is the process of finding different Virtual DOM in React updates. We usually compare the tree structure algorithms of two N nodes. the time complexity is O (n * 3 ).
By default, if there are too many nodes, too many operations are required, and React cannot adopt this algorithm;
The actual time complexity of the React algorithm is O (N) (the time complexity is only an estimate of the number of command operations required for an algorithm in the best and worst cases)
The Reconciliation algorithm of React is not complex. First, check whether the types of the two tree root nodes are the same. There are different processing methods based on the same or different types:
Different Node types
If the types of Tree nodes are not the same, it means that the changes are very large and the original tree structure is useless. You can discard it and need to create a new DOM tree, the original React component in the tree will experience the "unmount" lifecycle;
That is to say, for the Virtual DOM tree, this is an "Update" process, but it may lead to the "LOAD" and "Unload" processes of some components in this tree structure.
For example:
Before update
<div> <Todos /> </div>
We want to update it like this:
<span> <Todos /> </span>
> 1. in comparison, we can see that the root node is originally a div and the new one is a span, and the type is different. Therefore, this algorithm discards the previous div and includes all the child nodes in it, create a new span node and subnode;
> 2. obviously, it is a waste of time to re-build all the sub-nodes because of different root nodes, but to avoid the time complexity of O (N * 3, react: this is a simple and quick method;
> 3. As a developer, we must avoid the above waste.
If the node types are the same
If the two node types are the same, for DOM elements, React retains the DOM elements corresponding to the node, only compares the attributes and content of the node, and then modifies only the updated part;
When the node type is the same, for the React component type, React updates the node's component instance based on the props of the new node, triggering the component update process;
After processing the root node comparison, the React algorithm will repeat the same operation on each subnode of the root node.
Multiple identical child components
If the initial component Status is:
<ul> <TodoItem text = "First" /> <TodoItem text = "Second" /></ul>
Updated:
<ul> <TodoItem text = "First" /> <TodoItem text = "Second" /> <TodoItem text = "Third" /></ul>
React will create a new TodoItem component instance, while the first two will perform the normal update process. However, if it is:
<ul> <TodoItem text = "Zero" /> <TodoItem text = "First" /> <TodoItem text = "Second" /></ul>
(This will expose a problem) the ideal solution is to create a new TodoItem component instance first, and the last two will go into the natural update process.
However, for react to follow this method, you must find the differences between the two sub-components. The existing algorithm for calculating the differences between the two sequences is O (N * 2). Obviously
It is not suitable for scenarios with high performance requirements. Therefore, React selects a silly method to compare each sub-component;
React First changes the text of the component whose text is First to Zero, and the Second to First, and finally creates a component whose text is Second. This will break through the original two components to complete an update process, and create a new component whose text is Second.
This is obviously a waste, and the React is also intended and can be overcome. However, developers need to provide some help, which is the key.
Key Usage
The key attribute clearly tells the React unique identifier of each component.
If the initial component Status is:
<ul> <TodoItem key={1} text = "First" /> <TodoItem key={2} text = "Second" /></ul>
Updated:
<ul> <TodoItem key={0} text = "Zero" /> <TodoItem key={1} text = "First" /> <TodoItem key={2} text = "Second" /></ul>
Because there is a unique identifier key, React can know that the second and third components are the first and second components based on the key value, and then start the update process with the original props, in this way, shouldComponentUpdate will take effect to avoid unnecessary updates;
Note: As the unique identifier of a component, the key must be unique and unchangeable.
The following code is an incorrect example:
<ul> todos.map((item,index) => { <TodoItem key={index} text={item.text} /> })</ul>
The array subscript is used as the key value. It looks unique but unstable, because with the todos array value, the subscript of the array may be different in different update processes of the same component instance, using subscript as a React will make React messy. Remember that keys must be unique and unstable.
Note: although the key is a prop, the component that accepts the key cannot read the key value, because the key and ref are two special prop reserved by React, it is not expected that the group will be accessed directly.
The above is all the content of this article. I hope it will be helpful for your learning and support for helping customers.