preface

As we all know, DOM manipulation in browsers is very expensive and wasteful of performance!

Vue optimizes this part of performance waste by means of virtual DOM. Its core principle is to compare the difference between old and new nodes through diff algorithm, and determine which nodes can be reused to reduce the waste of DOM operations and improve performance!

So, the essence of the diff algorithm is to find the difference between two VNodes and reuse nodes as much as possible!

Why the virtual DOM

The vue writers actually have their own answer:

1. Opens the door to functional UI programming;

2. Backend can be rendered to objects other than DOM.

Talk about your understanding of these two points:

1. Opens the door to functional UI programming;

It is too expensive to refresh the page every time a new UI is generated. The introduction of virtual DOM and diff algorithm can reuse the old DOM to the maximum extent, resulting in a significant improvement in rendering performance.

2. Backend can be rendered to objects other than DOM.

With the virtual DOM, it’s easy to implement cross-platform, with the same core across multiple platforms, just render different on specific platforms

The essence of the diff algorithm is to compare the similarities and differences between VNodes. What attributes do Vnodes contain?

If you think about it, a DOM node consists of three main parts:

<div id='node'> <p id='diff'> haha </p> </div>Copy the code

1. Own tag name (div)

2, own attributes (id=’node’)

3. Child node (P)

So we can design the following object structure to represent a DOM node

Const oldVnode = {tag: 'div', attrs: {id: 'node'}, children: [{tag: 'p', attrs: {id: 'diff'}, children: [' ha ha ']}]}Copy the code

When the user performs operations on the interface, such as changing the ID of div to DOM and changing the text child node of SPAN from ‘haha’ to ‘hee hee’, we can get the following VNodes

Const newVnode = {tag: 'div', attrs: {id: 'dom'}, children: [{tag: 'p', attrs: {id: 'diff'}, children: [' hee hee ']}]}Copy the code

If we run diff(oldVnode,newVnode), we can see the difference between oldVnode and newVnode as follows: div id = ‘haha’; dom span text = ‘haha’;

document.getElementById("app").setAttribute('id', 'app2')// id changed to app2 document.getelementById ("child").firstChild.textContent ='2' //1 changed to 2Copy the code

The whole process above is a rehearsal of the whole diff process, the real diff process is much the same as the whole process above, but with more boundary conditions to consider! Let’s take a look at the real Diff algorithm

Timing of diff algorithm execution

Before understanding the execution timing of diff algorithm, let’s take a brief look at the rendering process of Vue framework, as shown below:

As can be seen from the figure, when we update the value of responsive data in data, the patch() function of VUE will compare the similarities and differences between the old and new Vnodes through diff algorithm during the execution, and then update the VNode tree. After the comparison is completed, the changed virtual contacts will be uniformly rendered into front-end pages.

The diff algorithm

Diff algorithm consists of two parts:

1. Vnode tree traversal

2. Node comparison: only nodes of the same layer are compared

Vnode tree traversal:

The virtual DOM is essentially just a tree structure, and tree traversal we know has depth traversal and breadth traversal

At present, both VUE and React use the deep traversal algorithm

Deep traversal requires a stack structure, which can be achieved either recursively (the kernel maintains the call stack), or by manually constructing the stack and then iterating through the stack. Usually, the depth-first search method does not reserve all the nodes, and the extended nodes are removed from the stack. In this way, the nodes stored in the stack are the depth value, so it occupies less space.

Node comparison:

For the same node, continue to compare child nodes:

Set startIndex and endIndex respectively for the new and old virtual DOM lists of child elements of the same level. First, compare the old start with the new start, compare the old end with the new end, and then cross-judge whether startIndex and endIndex are the same element

The result of comparison has three cases: same, new, delete, shift

The same

Remain the same

new

The old startIndex does not move, and the new startIndex shifts and is inserted in front of the old startIndex element

Shift:

The new startIndex is the same as the old startIndex or the new endIndex is the same as the old endIndex. Just move the startIndex or endIndex.

New startIndex is the same as old endIndex, new startIndex++, old endIndex–, insert ele of old endIndex in front of ele of old startIndex

New endIndex is the same as old startIndex, new endIndex–, old startIndex++, insert old startIndex ele after old endIndex ele

OldCh [idxInOld] = undefined; oldCh[idxInOld] = undefined;

delete

When the new startIndex and endIndex are closed, all the non-undefined vnodes between the old startIndex and endIndex will be removed, and the undefined node will represent the processed node (shift).

For example

1. The oldVnode and newVnode head Pointers point to the same node. The DOM node remains unchanged, and the oldVnode and newVnode head Pointers are one bit back and one bit forward respectively.

2. The oldVnode and newVnode tail pointer point to the same node, the DOM node remains unchanged, oldVnode and newVnode tail pointer backward one bit respectively.

3. The tail pointer node of newVnode and the head pointer node of oldVnode are the same node. After inserting the DOM corresponding to the head pointer of oldVnode into the DOM corresponding to the tail pointer of oldVnode, the tail pointer of newVnode moves back one bit and the head pointer of oldVnode moves forward one bit.

(4) oldVnode contains newVnode head pointer node. Before inserting the DOM corresponding to the tail pointer of newVnode into the DOM corresponding to the head pointer of oldVnode, oldCh head pointer moves forward one bit and newCh tail pointer moves backward one bit.

5. NewVnode does not contain the oldVnode header pointer node, delete the DOM corresponding to the oldVnode header pointer, move the newCh header pointer forward one bit, and move the oldCh tail pointer backward one bit.

6. OldVnode does not contain the newVnode header pointer node. Insert the newVnode header pointer node into the DOM corresponding to the oldVnode header pointer, and the newCh header pointer moves forward one bit.

7. NewVnode is gone, and the remaining oldVnode nodes 4, 6, and 7 have been deleted.

Finally, DOM is completely updated to the same structure as newNode, and the diff process is complete!

reference

1, www.jianshu.com/p/081103a62…

2, juejin. Cn/post / 684490…

3, www.jianshu.com/p/211c7f216…