SoFunction
Updated on 2025-04-05

A detailed explanation of virtual DOM and Diff algorithms in Vue

Virtual DOM

Virtual DOM (VDOM) is a programming concept that means "virtually" the UI required by the target is expressed through the data structure, saved in memory, and then the real one isDOMStay in sync with it. Specifically,Virtual DOMis a tree-like structure composed of a series of JavaScript objects, each representing aDOMElements, including the element's tag name, attributes, child nodes and other information.Virtual DOMEach node in it is a JavaScript object that can be easily created, updated, and destroyed without actually involvedDOMoperate.

Main functions

Virtual DOMThe main function is to change the data, and then the previous renderedVirtual DOMCompare, find out the changed parts, and update them with minimalActual DOM. This way can reduceActual DOMThe number of operations is improved to improve the performance and efficiency of page rendering.
Overall,Virtual DOMIt is a JavaScript object simulationReal DOMStructure and state technology, which operates in memoryVirtual DOM treeTo reduceActual DOMto improve page performance and user experience.

Virtual DOM tree

As the name implies, it means that a virtual DOM is the root node, which contains one or more sub-virtual DOMs.

Diff

In Vue 3, diff (difference comparison) refers to comparing the differences between the old and new virtual DOM trees when performing virtual DOM updates, and then only updating the actual changing parts to minimize operations on the real DOM and improve the performance and efficiency of the page.diffThe overall strategy is: depth priority, and comparison at the same level. In other words, comparison will only be carried out at the same level and will not be compared across levels; during the comparison process, the cycle will be closed from both sides to the middle.

Process analysis

The implementation process of the Diff algorithm can be summarized into the following steps:

  • Compare the root node:First, compare the root nodes of the old and new virtual DOM trees to determine whether they are the same.

  • Comparing child nodes layer by layer:If the root node is the same, then the child nodes are compared layer by layer.

    • Compare child node types:

      • If the node types are different, replace the entire node directly.
      • If the node type is the same, continue to compare the node's properties and events.
    • Compare the child node list:

      • Compare the new and old node lists through the double-pointer method to find nodes in the same position.
      • If the nodes are the same, recursively compare the child nodes.
      • If the nodes are different, perform operations to insert, delete or move the node according to the situation.
  • Handle new, deleted, and moved nodes:

    • If there are nodes that are not in the old node list in the new node list, perform the new operation.
    • If there are nodes that are not in the new node list in the old node list, perform a delete operation.
    • If the same node exists in the new and old node list, but in different orders, perform the operation of the mobile node.
  • Update node properties and events:

    • If the node is the same but the attributes or events change, update the node's properties and events.
  • Recursively compare child nodes:

    • If the node type is the same and is a container node (such as div, ul, etc.), then the child nodes are compared recursively.

Source code analysis

In the source code, patchVnode is where diff occurs. Here is the source code of patchVnode:

function patchVnode (oldVnode, vnode, insertedVnodeQueue, removeOnly) {
  // If the old and new nodes are consistent, do nothing  if (oldVnode === vnode) {
    return
  }

  // Let the reference to the current real dom, and when the el is modified, it will change synchronously  const elm =  = 

  // Asynchronous placeholder  if (isTrue()) {
    if (isDef()) {
      hydrate(, vnode, insertedVnodeQueue)
    } else {
       = true
    }
    return
  }
  // If the old and new are static nodes and have the same key  // When vnode is a cloned node or a node controlled by the v-once instruction, you only need to copy the sum to the vnode  // No need to do any other operations  if (isTrue() &&
    isTrue() &&
     ===  &&
    (isTrue() || isTrue())
  ) {
     = 
    return
  }

  let i
  const data = 
  if (isDef(data) && isDef(i = ) && isDef(i = )) {
    i(oldVnode, vnode)
  }

  const oldCh = 
  const ch = 
  if (isDef(data) && isPatchable(vnode)) {
    for (i = 0; i < ; ++i) [i](oldVnode, vnode)
    if (isDef(i = ) && isDef(i = )) i(oldVnode, vnode)
  }
  // If vnode is not a text node or annotation node  if (isUndef()) {
    // And all children    if (isDef(oldCh) && isDef(ch)) {
      // If the child nodes are not exactly consistent, updateChildren will be called      if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly)

      // If only the new vnode has children    } else if (isDef(ch)) {
      if (isDef()) (elm, '')
      // elm has referenced the old dom node and added child nodes on the old dom node      addVnodes(elm, null, ch, 0,  - 1, insertedVnodeQueue)

      // If the new vnode has no child nodes and vnode has child nodes, directly delete the old oldCh    } else if (isDef(oldCh)) {
      removeVnodes(elm, oldCh, 0,  - 1)

      // If the old node is a text node    } else if (isDef()) {
      (elm, '')
    }

    // If the new vnode and the old vnode are text nodes or comment nodes    // But when !=, you only need to update the text content.  } else if ( !== ) {
    (elm, )
  }
  if (isDef(data)) {
    if (isDef(i = ) && isDef(i = )) i(oldVnode, vnode)
  }
}

The above code is mainly used to compare old and new virtual DOM nodes and update them. Let me explain the implementation of this function step by step:

  • Determine whether updates are needed:First, the function will compare whether the new and old virtual DOM nodes are the same. If the same, it will return directly without subsequent operations.
  • Get the real DOM reference of the old node:passelm = = Put a new nodevnodeThe real DOM references the real DOM pointing to the old node.
  • Handling asynchronous placeholders:If the old node is an asynchronous placeholder (asyncPlaceholder), and the asynchronous factory of the new node has been parsed, thenhydrateThe function performs synchronous operations; otherwise, mark the new node as an asynchronous placeholder and return.
  • Handling static nodes:If the old and new nodes are static nodes (isStaticTrue), and with the same key, the component instance of the new node is referenced to the component instance of the old node.
  • Trigger the prepatch hook:If the new node's data object is definedhookandprepatchIf a hook exists, the hook function is executed to preprocess the differences between the old and new nodes.
  • Update node properties and events:If the new node's data object is definedhookandupdateIf a hook exists, the hook function is executed to update the properties and events of the node.
  • Processing child nodes:If the old and new nodes have children, compare the differences between them and update them, callupdateChildrenfunction. If only the new node has children, add the child node of the new node to the old node. If only the old node has children, delete the child nodes of the old node. If the old node is a text node, clear its contents.
  • Update text content:If the old and new nodes are text nodes or comment nodes, and their text content is different, the text content of the new node is updated.
  • Trigger postpatch hook:If the new node's data object is definedhookandpostpatchIf a hook exists, the hook function is executed to handle the operations after the node is updated.

Diff algorithm example

Here is a detailed example. Assume that there are the following two virtual DOM trees, we will compare them with the diff algorithm:

Old virtual DOM tree:

{
  type: 'div',
  props: { id: 'container' },
  children: [
    { type: 'p', props: { class: 'text' }, children: ['old Dom'] },
    { type: 'button', props: { disabled: true }, children: ['click'] }
  ]
}

New virtual DOM tree:

{
  type: 'div',
  props: { id: 'container' },
  children: [
    { type: 'p', props: { class: 'text' }, children: ['new DOM'] },
    { type: 'button', props: { disabled: false }, children: ['click'] },
    { type: 'span', props: { class: 'msg' }, children: ['msg'] }
  ]
}

Diff algorithm execution:

  • Compare the root node: The root node is the same, continue to compare the child nodes.

  • Compare child nodes:

    • The first child node has the same type but different contents, and the update content is 'new DOM'.
    • The second child node is the same, the property changes, and the disabled property is updated to false.
    • The third child node is a new node, which performs the insertion operation.
  • Update node properties and events: The properties of the second child node change, and update the disabled attribute.

  • Recursively compare child nodes: Continue to recursively compare their children nodes for the newly added span nodes.

Final result:

    {
  type: 'div',
  props: { id: 'container' },
  children: [
    { type: 'p', props: { class: 'text' }, children: ['new DOM'] },
    { type: 'button', props: { disabled: false }, children: ['click'] },
     { type: 'span', props: { class: 'msg' }, children: ['msg'] }
  ]
}

Conclusion

In general, the core idea of ​​the Diff algorithm is that Diff is to find the different points of the new and old virtual DOM and generate a patch, and generate update operations based on this patch to minimize operations on the actual DOM and improve the performance and efficiency of page rendering. Through the depth-first and same-layer comparison strategy, the Diff algorithm can efficiently handle the update of the virtual DOM tree, so that the page can quickly respond and update the corresponding views when data changes.

The above is a detailed explanation of the virtual DOM and Diff algorithms in Vue. For more information about Vue virtual DOM and Diff algorithms, please pay attention to my other related articles!