Understanding and summary of diff algorithm before react16

This article discusses the Diff algorithm before React 16. However, React 16 uses a new fiber architecture, and the corresponding Diff algorithm has also changed. This film does not discuss Fiber in detail.

The fiber architecture is to support react's interruptible rendering, reduce stuttering, and improve fluency.

In versions before react16, the diff virtual dom was completed in one go. This may cause stuttering, because the frame rate recognizable by the human eye is 1s 60 frames, that is, a frame of 16ms. If the diff time exceeds 16ms, the rendering will be blocked, and you will feel stuttering.

In order to avoid this situation, it is necessary to make the diff operation not exceed 16ms. If it exceeds 16ms, pause it first, let the browser perform the rendering operation, and continue the diff after the subsequent rendering gap.

The fiber architecture is involved in supporting this "interruptible rendering". The fiber tree is a data structure that connects the virtual dom tree into a linked list, so that the traversal operation can support breakpoint restart.

The core idea of ​​React

The core of React is Virtual DOM and Diff algorithm. React maintains a virtual DOM tree in memory. When the data changes (state & props), it will automatically update the virtual DOM to obtain a new virtual DOM tree, and then use the Diff algorithm to compare the old and new virtual DOM trees to find out For the smallest changed part, add this changed part (Patch) to the queue, and finally update these patches to the actual DOM in batches.

Traditional diff algorithm

Map a Tree to another Tree through the minimum number of operation steps. This algorithm is called Tree Edit Distance (tree edit distance). As shown in the picture:

In the figure above, the minimum number of operation steps (edit distance) is 3:

  1. delete ul node
  2. Add span node
  3. add text node

The Tree Edit Distance algorithm has been developed and evolved for more than 30 years from 1979 to 2011, and its time complexity was finally optimized to O(n^3). Its development process is roughly as follows (n is the total number of nodes in the tree):

  1. In 1979, Tai proposed the second non-power level complexity algorithm, the time complexity is O(m3*n3)
  2. In 1989, Zhang and Shasha optimized Tai's algorithm with a time complexity of O(m2*n2)
  3. In 1998, Klein optimized the algorithm of Zhang and Shasha again, and the time complexity was O(n^2*m*log(m))
  4. In 2009, Demiane proposed the calculation formula in the worst case, controlling the time complexity to O(n^2*m*(1+log(m/n)))
  5. In 2011, Pawlik and N.Augsten proposed an algorithm suitable for trees of all shapes, and controlled the time complexity to O(n^3)

The specific implementation and principle of the Tree Edit Distance algorithm will not be discussed here. If you are interested, you can directly read this paper  A Robust Algorithm for the Tree Edit Distance

React diff

The optimal solution of the time complexity of the traditional diff algorithm is O(n^3), so if there are 1000 nodes, a diff will perform 1 billion comparisons, which obviously cannot meet the requirements of high performance. React successfully transforms the problem of O(n^3) complexity into the problem of O(n) complexity through bold assumptions and related strategies based on the assumptions.

(1) Two assumptions

To optimize the diff algorithm, React makes two assumptions:

  1. Two different types of elements will produce different trees
  2. Developers can use  key props to hint which child elements are stable under different renderings

(2) Three strategies

Based on the above two assumptions, React proposes three strategies to optimize the diff algorithm:

  1. The cross-level movement operations of DOM nodes in the Web UI are extremely rare and can be ignored
  2. Two components with the same type will generate similar tree structures, and two components with different types will generate different tree structures
  3. For a group of child nodes at the same level, they can be distinguished by a unique key

(3) diff specific optimization

Based on the above three strategies, React optimizes the diff algorithm for the following three parts respectively

  • tree diff
  • component diff
  • element diff

tree diff

React only performs hierarchical comparison of the virtual DOM tree, and does not consider the cross-level comparison of nodes. As shown below:

As shown in the figure above, React uses updateDepth to control the level of the virtual Dom tree, and only compares the nodes in the same color box, and adds and deletes nodes according to the comparison results. In this way, the entire comparison can be completed only by traversing the virtual Dom tree once.

If a cross-level move operation occurs, as shown in the figure below:

Through hierarchical comparison, it can be seen that React will not reuse B node and its child nodes, but will directly delete the B node under the A node, and then create a new B node and its child nodes under the C node. Therefore, if a cross-level operation occurs, React cannot reuse existing nodes, which may cause React to perform a large number of re-creation operations, which will affect performance. Therefore, React officially recommends avoiding cross-level operations as much as possible.

component diff

React is built based on components, and the strategy adopted for the comparison between components is as follows:

  • If it is the same type of component, first use  shouldComponentUpdate()the method to judge whether it needs to be compared, if it returns true, continue to compare the virtual DOM tree of the component according to the React diff strategy, otherwise no need to compare
  • If it is a different type of component, judge the component as a dirty component, thereby replacing all child nodes under the entire component

 

As shown in the figure above, although components C and H have similar structures, they are of different types. React will not compare them, and will directly delete component C and create component H.

From the above component diff strategy, we can know:

  1. For different types of components, no comparison operation is required by default, and they are directly recreated.
  2. shouldComponentUpdate()For components of the same type, compare and optimize by allowing developers to customize methods to reduce unnecessary comparison of components. If there is no customization, shouldComponentUpdate()the method returns by default true. By default, every time the data (state & props) of the component changes, it will be compared.

element diff

element diff involves three operations: move, create, delete. For child nodes at the same level, discuss whether to use key or not.

For the case where the key is not used, as shown in the figure below:

React compares the old and new child nodes at the same level, and finds that B in the new collection is not equal to A in the old collection, so A is deleted, B is created, and so on, until D is deleted and C is created. This will make the same node cannot be reused, and frequent delete and create operations will occur, thus affecting performance.

For the case of using key, as shown in the figure below:

The case of using key

React will first traverse the new collection, and use the unique key to determine whether the same node exists in the old collection, if not, create it, and if so, judge whether it needs to be moved. And React also uses a relatively efficient algorithm for mobile operations, using a sequential optimization method, which will not be discussed in detail here.

From the above, we can see that element diff is to optimize the diff through the unique key, and reduce the deletion and creation of nodes by reusing existing nodes.

(4) How to perform diff

The diff strategy and specific optimization of React have been discussed above, here is a brief talk about how React applies these strategies to diff:

React is built based on components. First, the entire virtual DOM tree can be abstracted into a React component tree (each component is composed of a smaller component tree, and so on), and the React diff strategy can be applied to compare this component tree. , if one of the components needs to be compared, treat this component as a smaller component tree, continue to use the React diff strategy to compare this smaller component tree, and so on, until all the components that need to be compared are traversed in the hierarchy.

summary

React uses bold assumptions to formulate corresponding diff strategies to convert O(n3) complexity problems into O(n) complexity problems

  • Algorithm optimization of tree diff through hierarchical comparison strategy
  • Generate similar tree structures through the same class, and generate different tree structures and strategies from different classes shouldComponentUpdate, and optimize the algorithm for component diff
  • Algorithm optimization for element diff by setting a unique key strategy

In summary, tree diff and component diff reduce the complexity of the algorithm from the top-level design, while element diff is further optimized in more details.

 

Guess you like

Origin blog.csdn.net/weixin_44786530/article/details/132690892