[Vue Principle Analysis] Virtual DOM

Vue.js is a popular JavaScript framework that uses the concept of virtual DOM to improve performance and development efficiency. Virtual DOM is one of the cores of Vue.js. It builds a lightweight DOM tree in memory instead of directly operating the real DOM, thereby reducing the number of operations on the real DOM and improving page rendering efficiency. This article will deeply explore the role of virtual DOM and core source code analysis in Vue.js.

The role of virtual DOM

Virtual DOM is a lightweight JavaScript object that represents the structure and status of the entire page in a tree structure. When the page changes, Vue.js will update the page by comparing the differences between the old and new virtual DOM trees and applying the differences to the real DOM. This method has the following advantages compared to directly operating the real DOM:

1. Improve performance

Since direct manipulation of the real DOM requires frequent rearrangement and redrawing, the virtual DOM can update differences in batches, reducing the number of operations on the real DOM, thereby improving page rendering efficiency.

2. Simplify development

By using a virtual DOM, developers can shift their focus from manually manipulating the real DOM to higher-level logic. Developers only need to pay attention to the structure and status of the page, and do not need to care about specific DOM operation details, thus simplifying the development process.

3. Cross-platform support

Since the virtual DOM is a platform-independent JavaScript object, it can be used on different platforms. This means developers can use the same code base to build web, mobile and desktop applications.

Source code analysis

In Vue.js, virtual DOM is represented by VNode (Virtual Node) object. The VNode object is a pure JavaScript object, which contains the node’s label name, attributes, child nodes and other information. Vue.js builds the real DOM by recursively traversing the VNode tree, and updates the page by comparing the differences between the old and new VNode trees.

The patch function is defined in the src/core/vdom/patch.js file. It is the core function that applies new VNode objects to old VNode objects to update the page. The following is part of the code of the patch function:


export function patch(oldVnode, vnode, hydrating, removeOnly) {
  // ...
  const isRealElement = isDef(oldVnode.nodeType)
  if (!isRealElement & amp; & amp; sameVnode(oldVnode, vnode)) {
    // If the old and new VNode are the same node, call the patchVnode function to compare and update
    patchVnode(oldVnode, vnode, insertedVnodeQueue, null, null, removeOnly)
  } else {
    // If the old and new VNodes are not the same node, destroy the old node and create a new one
    const oldElm = oldVnode.elm
    const parentElm = nodeOps.parentNode(oldElm)
    createElm(
      vnode,
      insertedVnodeQueue,
      oldElm._leaveCb ? null : parentElm,
      nodeOps.nextSibling(oldElm)
    );
    //destroy old node
    if (isDef(parentElm)) {
      removeVnodes([oldVnode], 0, 0)
    } else if (isDef(oldVnode.tag)) {
      invokeDestroyHook(oldVnode)
    }
  }
  // ...
}

In the patch function, first determine whether the old and new VNode are the same node by calling the sameVnode function. If it is the same node, call the patchVnode function to compare and update; if it is not the same node, destroy the old node and create a new node.

Next, let’s take a look at part of the code of the patchVnode function:


function patchVnode(oldVnode, vnode, insertedVnodeQueue, removeOnly) {
  // ...
  if (isDef(data) & amp; & amp; isPatchable(vnode)) {
    // Compare and update properties
    for (i = 0; i < cbs.update.length; + + i) cbs.update[i](oldVnode, vnode);
    if (isDef((i = data.hook)) & amp; & amp; isDef((i = i.update))) i(oldVnode, vnode);
  }
  if (isUndef(vnode.text)) {
    if (isDef(oldCh) & amp; & amp; isDef(ch)) {
      // Compare and update child nodes
      if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly);
    } else if (isDef(ch)) {
      //Add new child node
      // ...
      addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue);
    } else if (isDef(oldCh)) {
      //Remove old child nodes
      removeVnodes(elm, oldCh, 0, oldCh.length - 1);
    }
  } else if (oldVnode.text !== vnode.text) {
    //Update text content
    nodeOps.setTextContent(elm, vnode.text);
  }
  // ...
}

In the patchVnode function, the properties of the VNode are first compared and updated. Compare and update properties by traversing the cbs.update array and calling the corresponding update function. If the VNode is not a text node, child nodes are compared and updated. Compare and update the old and new child nodes by calling the updateChildren function. Finally, if the VNode is a text node, the text content is updated directly.

Through the above code, we can see that in the Vue.js source code, the differences between the old and new VNodes are compared and updated through the patch function and the patchVnode function. During the comparison process, different processing will be performed according to the type of VNode, including comparison and update of attributes, comparison and update of child nodes, update of text content, etc.

This difference comparison method can efficiently apply the new VNode object to the old VNode object, and apply the difference to the real DOM, thereby realizing the updating and rendering of the virtual DOM. This can reduce the number of operations on the real DOM and improve page rendering efficiency.

When updating the page, Vue uses an efficient algorithm to compare the differences between the old and new VNode trees. This algorithm converts the VNode tree into a patch array, which contains instructions that need to operate on the real DOM. Then, Vue.js updates the page by traversing the patch array and performing corresponding operations on the real DOM according to the instructions.

Simple example explanation

Old VNode:

Hello, Vue!

New VNode:

Hello, Vue.js!Extra content

  1. First, compare the old VNode with the new VNode. Compare tag names and attributes.

    The tag names are the same but the attributes are different:

    • The id attribute of the old VNode is “old”
    • The id attribute of the new VNode is “new”`
  2. Add the differences to the patches array.

    Patch array: [{ type: 'props', prop: 'id', value: 'new' }]

  3. Compare child nodes.

    Child nodes are different:

    • The old VNode had a text node: “Hello, Vue!”
    • The new VNode has a text node: “Hello, Vue.js!”, and a child node Extra content
  4. Add the differences to the patches array.

    Patch array:

    
    
    [
        { type: 'props', prop: 'id', value: 'new' },
        { type: 'text', value: 'Hello, Vue.js!' },
        { type: 'insert', parentElm: ..., refElm: ..., vnode: ... }
    ]
    

Through the above example, we can see that when comparing old and new VNodes, their tag names, attributes, and child nodes are compared one by one, and the differences are added to the patch array. This patch array describes the differences between the old and new VNodes and can be used for subsequent update operations.

Summary

Virtual DOM is an important and core concept in Vue.js. It improves performance and development efficiency by building a lightweight DOM tree in memory instead of directly operating the real DOM. Analysis of the core source code of Virtual DOM reveals how Vue.js updates the page by comparing the differences between the old and new VNode trees. By deeply understanding the principles of virtual DOM, developers can better take advantage of the functions and features provided by Vue.js to build high-performance and maintainable web applications.

The knowledge points of the article match the official knowledge files, and you can further learn relevant knowledge. Vue entry skill treeHomepageOverview 39849 people are learning the system