


Introduction 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!
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
#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:
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!
# 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.a
After 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.f
After 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.b
After 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.e
After 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.
g
After 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.
h
After 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 isc,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
会有什么帮助呢?这边引用一张图:
有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!

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.

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.

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.

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.

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

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 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 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.


Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

AI Hentai Generator
Generate AI Hentai for free.

Hot Article

Hot Tools

VSCode Windows 64-bit Download
A free and powerful IDE editor launched by Microsoft

EditPlus Chinese cracked version
Small size, syntax highlighting, does not support code prompt function

SublimeText3 Linux new version
SublimeText3 Linux latest version

Dreamweaver CS6
Visual web development tools

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