search
HomeWeb Front-endJS TutorialIntroduction to the principle of virtual dom comparison in Vue (example explanation)

This article brings you an introduction to the virtual dom comparison principle in Vue (explanation with examples). It has certain reference value. Friends in need can refer to it. I hope it will be helpful to you.

Let’s first talk about why there is a virtual dom comparison stage. We know that Vue is a data-driven view (changes in data will cause changes in the view), but you find that when a certain data changes, the view is partial How to accurately find the view corresponding to the data and update it by refreshing instead of re-rendering the whole thing? Then you need to get the DOM structure before and after the data change, find the differences and update it!

Virtual dom is essentially a simple object extracted from the real dom. Just like a simple p contains more than 200 attributes, but what is really needed may only be tagName, so direct operations on the real dom will greatly affect performance!

Introduction to the principle of virtual dom comparison in Vue (example explanation)

The simplified virtual node (vnode) roughly contains the following attributes:

{
  tag: 'p',       // 标签名
  data: {},         // 属性数据,包括class、style、event、props、attrs等
  children: [],     // 子节点数组,也是vnode结构
  text: undefined,  // 文本
  elm: undefined,   // 真实dom
  key: undefined    // 节点标识
}

The comparison of virtual dom is to find out the new node (vnode ) and the old node (oldVnode), and then patch the difference. The general process is as follows

Introduction to the principle of virtual dom comparison in Vue (example explanation)

#The whole process is relatively simple. If the old and new nodes are not similar, create DOM directly based on the new node; if they are similar, first compare the data, including class and style. , event, props, attrs, etc., if there is any difference, call the corresponding update function, and then compare the child nodes. The comparison of child nodes uses diff algorithm, which should be the focus and difficulty of this article. Bar.

It is worth noting that during the Children Compare process, if similar childVnode is found, they will recursively enter a new window. patching process.

Source code analysis

This time the source code analysis is written more concisely. If I write too much, I find myself unwilling to read it (┬_┬)

Start

Let’s look at the patch() function first:

function patch (oldVnode, vnode) {
  var elm, parent;
  if (sameVnode(oldVnode, vnode)) {
    // 相似就去打补丁(增删改)
    patchVnode(oldVnode, vnode);
  } else {
    // 不相似就整个覆盖
    elm = oldVnode.elm;
    parent = api.parentNode(elm);
    createElm(vnode);
    if (parent !== null) {
      api.insertBefore(parent, vnode.elm, api.nextSibling(elm));
      removeVnodes(parent, [oldVnode], 0, 0);
    }
  }
  return vnode.elm;
}

patch() The function receives two parameters, the old and new vnode. There is a big difference between the two parameters passed in. Difference: oldVnode's elm points to the real dom, while vnode's elm is undefined... But after the patch() method, vnode's elm will also point to this (updated) real dom.

The sameVnode() method to determine whether the old and new vnodes are similar is very simple, which is to compare whether tag and key are consistent.

function sameVnode (a, b) {
  return a.key === b.key && a.tag === b.tag;
}

Patch

The solution to the inconsistency between the old and new vnodes is very simple, that is, create a real dom based on the vnode and replace the elm insertion in the oldVnode DOM document.

For the consistent processing of old and new vnodes is the patching we often mentioned before. What exactly is patching? Just look at the patchVnode() method:

function patchVnode (oldVnode, vnode) {
  // 新节点引用旧节点的dom
  let elm = vnode.elm = oldVnode.elm;
  const oldCh = oldVnode.children;
  const ch = vnode.children;

  // 调用update钩子
  if (vnode.data) {
    updateAttrs(oldVnode, vnode);
    updateClass(oldVnode, vnode);
    updateEventListeners(oldVnode, vnode);
    updateProps(oldVnode, vnode);
    updateStyle(oldVnode, vnode);
  }

  // 判断是否为文本节点
  if (vnode.text == undefined) {
    if (isDef(oldCh) && isDef(ch)) {
      if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue)
    } else if (isDef(ch)) {
      if (isDef(oldVnode.text)) api.setTextContent(elm, '')
      addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue)
    } else if (isDef(oldCh)) {
      removeVnodes(elm, oldCh, 0, oldCh.length - 1)
    } else if (isDef(oldVnode.text)) {
      api.setTextContent(elm, '')
    }
  } else if (oldVnode.text !== vnode.text) {
    api.setTextContent(elm, vnode.text)
  }
}

Patching is actually calling various updateXXX() functions to update various attributes of the real dom. Each update function is similar, take updateAttrs() as an example:

function updateAttrs (oldVnode, vnode) {
  let key, cur, old
  const elm = vnode.elm
  const oldAttrs = oldVnode.data.attrs || {}
  const attrs = vnode.data.attrs || {}

  // 更新/添加属性
  for (key in attrs) {
    cur = attrs[key]
    old = oldAttrs[key]
    if (old !== cur) {
      if (booleanAttrsDict[key] && cur == null) {
        elm.removeAttribute(key)
      } else {
        elm.setAttribute(key, cur)
      }
    }
  }
  // 删除新节点不存在的属性
  for (key in oldAttrs) {
    if (!(key in attrs)) {
      elm.removeAttribute(key)
    }
  }
}

The general idea of ​​the update function of the attribute (Attribute) is:

  • Traverse the vnode attribute, if it is different from oldVnode, call setAttribute() to modify;

  • Traverse the oldVnode attribute, if it is not the same Call removeAttribute() to delete the vnode attribute.

You will find that there is a judgment of booleanAttrsDict[key], which is used to judge whether it is in the Boolean type attribute dictionary.

['allowfullscreen', 'async', 'autofocus', 'autoplay', 'checked', 'compact', 'controls', 'declare', ...]

eg : <video autoplay></video>, if you want to turn off autoplay, you need to remove this attribute.

After all the data is compared, it’s time to compare the child nodes. First determine whether the current vnode is a text node. If it is a text node, there is no need to consider the comparison of child nodes; if it is an element node, it needs to be considered in three situations:

  • Both old and new nodes have children. , then enter the comparison of child nodes (diff algorithm);

  • The new node has children, but the old node does not, then create the dom node in a loop;

  • The new node does not have children, but the old node does, then delete the dom node in a loop.

The latter two situations are relatively simple. We directly analyze the first situation, comparison of child nodes.

diff algorithm

There are many codes in this part of sub-node comparison. Let’s talk about the principle first and then post the code. First look at a picture comparing child nodes:

Introduction to the principle of virtual dom comparison in Vue (example explanation)

oldCh and newCh in the figure represent the old and new child node arrays respectively. They have their own head and tail pointers oldStartIdx, oldEndIdx, newStartIdx, newEndIdx, vnode is stored in the array. For easy understanding, it is replaced by a, b, c, d, etc., which represent different types of tags (p, span, p) vnode object.

The comparison of child nodes is essentially a loop comparison of head and tail nodes. The sign of the end of the loop is: the old child node array or the new child node array has been traversed (i.e. oldStartIdx > oldEndIdx || newStartIdx > newEndIdx). Take a rough look at the cycle process:

  • The first step compare head to head. If they are similar, the old and new head pointers move backward (i.e. oldStartIdx && newStartIdx ), the real DOM remains unchanged, and the next cycle is entered; if they are not similar, enter the second step.

  • The second step Compare tail to tail. If they are similar, the old end and new end pointers are moved forward (i.e. oldEndIdx-- && newEndIdx--), the real DOM remains unchanged, and the next cycle is entered; if they are not similar, the third step is entered. .

  • The third step Compare head to tail. If they are similar, the old head pointer moves backward and the new tail pointer moves forward (i.e. oldStartIdx && newEndIdx--). The head in the unconfirmed dom sequence is moved to the end and the next cycle is entered. ; Not similar, go to step four.

  • Step 4 Compare tail and head. If they are similar, the old tail pointer is moved forward and the new head pointer is moved backward (i.e. oldEndIdx-- && newStartIdx ). The tail in the unconfirmed dom sequence is moved to the head and the next cycle is entered; Not similar, go to step five.

  • The fifth step is that if the node has a key and the sameVnode is found in the old child node array (both tag and key are consistent), then move its dom to the head of the current real dom sequence. The new head pointer moves backward (i.e. newStartIdx ); otherwise, the dom corresponding to the vnode (vnode[newStartIdx].elm) is inserted into the head of the current real dom sequence, and the new head pointer moves backward (i.e. newStartIdx ).

Let’s take a look at the situation without the key first, and put an animation to see it more clearly!

Introduction to the principle of virtual dom comparison in Vue (example explanation)

# I believe that after reading the picture, you will have a better understanding of the essence of the diff algorithm. The whole process is relatively simple. In the picture above, a total of 6 cycles have been entered, involving each situation. Let’s describe them one by one:

  • The first time they are all similar (both are a), The dom does not change, and both the old and new head pointers move backward. aAfter the node is confirmed, the real dom sequence is: a,b,c,d,e,f, the unconfirmed dom sequence is: b,c,d,e,f

  • The second time the tail and tail are similar (both are f), the dom does not change, and the old and new tail pointers are moved forward. fAfter the node is confirmed, the real dom sequence is: a,b,c,d,e,f, the unconfirmed dom sequence is: b,c,d,e ;

  • The third time is that the head and tail are similar (both are b). The head in the current remaining real dom sequence is moved to the end, the old head pointer is moved backward, and the new head pointer is moved backward. The tail pointer moves forward. bAfter the node is confirmed, the real dom sequence is: a,c,d,e,b,f, the unconfirmed dom sequence is: c,d,e;

  • The fourth time is that the tail and head are similar (both are e). The tail in the current remaining real dom sequence is moved to the head, the old tail pointer is moved forward, and the new head pointer is moved behind shift. eAfter the node is confirmed, the real dom sequence is: a,e,c,d,b,f, the unconfirmed dom sequence is: c,d;

  • The fifth time is not similar and is directly inserted into the head of the unconfirmed dom sequence. gAfter the node is inserted, the real dom sequence is: a,e,g,c,d,b,f, the unconfirmed dom sequence is: c,d;

  • The sixth time is not similar and is directly inserted into the head of the unconfirmed dom sequence. hAfter the node is inserted, the real dom sequence is: a,e,g,h,c,d,b,f, and the unconfirmed dom sequence is: c,d

But after ending the loop, there are two situations to consider:

  • The new byte point array (newCh) has been traversed (newStartIdx > newEndIdx). Then you need to delete all the redundant old dom (oldStartIdx -> oldEndIdx). In the above example, it is c,d;

  • new The byte point array (oldCh) has been traversed (oldStartIdx > oldEndIdx). Then you need to add all the extra new dom (newStartIdx -> newEndIdx).

上面说了这么多都是没有key的情况,说添加了:key可以优化v-for的性能,到底是怎么回事呢?因为v-for大部分情况下生成的都是相同tag的标签,如果没有key标识,那么相当于每次头头比较都能成功。你想想如果你往v-for绑定的数组头部push数据,那么整个dom将全部刷新一遍(如果数组每项内容都不一样),那加了key会有什么帮助呢?这边引用一张图:

Introduction to the principle of virtual dom comparison in Vue (example explanation)

key的情况,其实就是多了一步匹配查找的过程。也就是上面循环流程中的第五步,会尝试去旧子节点数组中找到与当前新子节点相似的节点,减少dom的操作!

有兴趣的可以看看代码:

function updateChildren (parentElm, oldCh, newCh) {
  let oldStartIdx = 0
  let newStartIdx = 0
  let oldEndIdx = oldCh.length - 1
  let oldStartVnode = oldCh[0]
  let oldEndVnode = oldCh[oldEndIdx]
  let newEndIdx = newCh.length - 1
  let newStartVnode = newCh[0]
  let newEndVnode = newCh[newEndIdx]
  let oldKeyToIdx, idxInOld, elmToMove, before

  while (oldStartIdx  oldEndIdx) {
    before = isUndef(newCh[newEndIdx+1]) ? null : newCh[newEndIdx + 1].elm
    addVnodes(parentElm, before, newCh, newStartIdx, newEndIdx, insertedVnodeQueue)
  } else if (newStartIdx > newEndIdx) {
    removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx)
  }
}

The above is the detailed content of Introduction to the principle of virtual dom comparison in Vue (example explanation). For more information, please follow other related articles on the PHP Chinese website!

Statement
This article is reproduced at:segmentfault. If there is any infringement, please contact admin@php.cn delete
Python vs. JavaScript: Community, Libraries, and ResourcesPython vs. JavaScript: Community, Libraries, and ResourcesApr 15, 2025 am 12:16 AM

Python and JavaScript have their own advantages and disadvantages in terms of community, libraries and resources. 1) The Python community is friendly and suitable for beginners, but the front-end development resources are not as rich as JavaScript. 2) Python is powerful in data science and machine learning libraries, while JavaScript is better in front-end development libraries and frameworks. 3) Both have rich learning resources, but Python is suitable for starting with official documents, while JavaScript is better with MDNWebDocs. The choice should be based on project needs and personal interests.

From C/C   to JavaScript: How It All WorksFrom C/C to JavaScript: How It All WorksApr 14, 2025 am 12:05 AM

The shift from C/C to JavaScript requires adapting to dynamic typing, garbage collection and asynchronous programming. 1) C/C is a statically typed language that requires manual memory management, while JavaScript is dynamically typed and garbage collection is automatically processed. 2) C/C needs to be compiled into machine code, while JavaScript is an interpreted language. 3) JavaScript introduces concepts such as closures, prototype chains and Promise, which enhances flexibility and asynchronous programming capabilities.

JavaScript Engines: Comparing ImplementationsJavaScript Engines: Comparing ImplementationsApr 13, 2025 am 12:05 AM

Different JavaScript engines have different effects when parsing and executing JavaScript code, because the implementation principles and optimization strategies of each engine differ. 1. Lexical analysis: convert source code into lexical unit. 2. Grammar analysis: Generate an abstract syntax tree. 3. Optimization and compilation: Generate machine code through the JIT compiler. 4. Execute: Run the machine code. V8 engine optimizes through instant compilation and hidden class, SpiderMonkey uses a type inference system, resulting in different performance performance on the same code.

Beyond the Browser: JavaScript in the Real WorldBeyond the Browser: JavaScript in the Real WorldApr 12, 2025 am 12:06 AM

JavaScript's applications in the real world include server-side programming, mobile application development and Internet of Things control: 1. Server-side programming is realized through Node.js, suitable for high concurrent request processing. 2. Mobile application development is carried out through ReactNative and supports cross-platform deployment. 3. Used for IoT device control through Johnny-Five library, suitable for hardware interaction.

Building a Multi-Tenant SaaS Application with Next.js (Backend Integration)Building a Multi-Tenant SaaS Application with Next.js (Backend Integration)Apr 11, 2025 am 08:23 AM

I built a functional multi-tenant SaaS application (an EdTech app) with your everyday tech tool and you can do the same. First, what’s a multi-tenant SaaS application? Multi-tenant SaaS applications let you serve multiple customers from a sing

How to Build a Multi-Tenant SaaS Application with Next.js (Frontend Integration)How to Build a Multi-Tenant SaaS Application with Next.js (Frontend Integration)Apr 11, 2025 am 08:22 AM

This article demonstrates frontend integration with a backend secured by Permit, building a functional EdTech SaaS application using Next.js. The frontend fetches user permissions to control UI visibility and ensures API requests adhere to role-base

JavaScript: Exploring the Versatility of a Web LanguageJavaScript: Exploring the Versatility of a Web LanguageApr 11, 2025 am 12:01 AM

JavaScript is the core language of modern web development and is widely used for its diversity and flexibility. 1) Front-end development: build dynamic web pages and single-page applications through DOM operations and modern frameworks (such as React, Vue.js, Angular). 2) Server-side development: Node.js uses a non-blocking I/O model to handle high concurrency and real-time applications. 3) Mobile and desktop application development: cross-platform development is realized through ReactNative and Electron to improve development efficiency.

The Evolution of JavaScript: Current Trends and Future ProspectsThe Evolution of JavaScript: Current Trends and Future ProspectsApr 10, 2025 am 09:33 AM

The latest trends in JavaScript include the rise of TypeScript, the popularity of modern frameworks and libraries, and the application of WebAssembly. Future prospects cover more powerful type systems, the development of server-side JavaScript, the expansion of artificial intelligence and machine learning, and the potential of IoT and edge computing.

See all articles

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

AI Hentai Generator

AI Hentai Generator

Generate AI Hentai for free.

Hot Article

R.E.P.O. Energy Crystals Explained and What They Do (Yellow Crystal)
4 weeks agoBy尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Best Graphic Settings
4 weeks agoBy尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. How to Fix Audio if You Can't Hear Anyone
4 weeks agoBy尊渡假赌尊渡假赌尊渡假赌
WWE 2K25: How To Unlock Everything In MyRise
1 months agoBy尊渡假赌尊渡假赌尊渡假赌

Hot Tools

VSCode Windows 64-bit Download

VSCode Windows 64-bit Download

A free and powerful IDE editor launched by Microsoft

EditPlus Chinese cracked version

EditPlus Chinese cracked version

Small size, syntax highlighting, does not support code prompt function

SublimeText3 Linux new version

SublimeText3 Linux new version

SublimeText3 Linux latest version

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

DVWA

DVWA

Damn Vulnerable Web App (DVWA) is a PHP/MySQL web application that is very vulnerable. Its main goals are to be an aid for security professionals to test their skills and tools in a legal environment, to help web developers better understand the process of securing web applications, and to help teachers/students teach/learn in a classroom environment Web application security. The goal of DVWA is to practice some of the most common web vulnerabilities through a simple and straightforward interface, with varying degrees of difficulty. Please note that this software