Summary of diff algorithm usage skills
This time I will bring you a summary of the techniques for using the diff algorithm. What are the precautions when using the diff algorithm? The following is a practical case, let’s take a look.
Virtual dom
The diff algorithm must first clarify the concept that the object of diff is the virtual dom, and updating the real dom is the result of the diff algorithm
Vnode base class
constructor ( 。。。 ) { this.tag = tag this.data = data this.children = children this.text = text this.elm = elm this.ns = undefined this.context = context this.fnContext = undefined this.fnOptions = undefined this.fnScopeId = undefined this.key = data && data.key this.componentOptions = componentOptions this.componentInstance = undefined this.parent = undefined this.raw = false this.isStatic = false this.isRootInsert = true this.isComment = false this.isCloned = false this.isOnce = false this.asyncFactory = asyncFactory this.asyncMeta = undefined this.isAsyncPlaceholder = false }
This part of the code is mainly to better understand the meaning of the attribute of the specific diff in the diff algorithm. Of course, it can also be better understood Understand the vnode instance
The overall process
The core function is the patch function
isUndef judgment (whether it is undefined or null)
// empty mount (likely as component), create new root elementcreateElm(vnode, insertedVnodeQueue) Here you can find that creating nodes is not inserted one by one, but put into a queue for unified batch processing
Core function sameVnode
function sameVnode (a, b) { return ( a.key === b.key && ( ( a.tag === b.tag && a.isComment === b.isComment && isDef(a.data) === isDef(b.data) && sameInputType(a, b) ) || ( isTrue(a.isAsyncPlaceholder) && a.asyncFactory === b.asyncFactory && isUndef(b.asyncFactory.error) ) ) ) }
Here is an outer comparison function that directly compares the keys and tags of the two nodes. ), compare data (note that data here refers to VNodeData), and directly compare types for input.
export interface VNodeData { key?: string | number; slot?: string; scopedSlots?: { [key: string]: ScopedSlot }; ref?: string; tag?: string; staticClass?: string; class?: any; staticStyle?: { [key: string]: any }; style?: object[] | object; props?: { [key: string]: any }; attrs?: { [key: string]: any }; domProps?: { [key: string]: any }; hook?: { [key: string]: Function }; on?: { [key: string]: Function | Function[] }; nativeOn?: { [key: string]: Function | Function[] }; transition?: object; show?: boolean; inlineTemplate?: { render: Function; staticRenderFns: Function[]; }; directives?: VNodeDirective[]; keepAlive?: boolean; }
This will confirm whether the two nodes have further comparison value, otherwise they will be replaced directly
The replacement process is mainly a createElm function and the other is to destroy the oldVNode
// destroy old node if (isDef(parentElm)) { removeVnodes(parentElm, [oldVnode], 0, 0) } else if (isDef(oldVnode.tag)) { invokeDestroyHook(oldVnode) }
Insert To simplify the process, it is to determine the type of the node and call
createComponent respectively (it will determine whether there are children and then call it recursively)
createComment
createTextNode
After creation After using the insert function
, you need to use the hydrate function to map the virtual dom and the real dom
function insert (parent, elm, ref) { if (isDef(parent)) { if (isDef(ref)) { if (ref.parentNode === parent) { nodeOps.insertBefore(parent, elm, ref) } } else { nodeOps.appendChild(parent, elm) } } }
Core function
function patchVnode (oldVnode, vnode, insertedVnodeQueue, removeOnly) { if (oldVnode === vnode) { return } const elm = vnode.elm = oldVnode.elm if (isTrue(oldVnode.isAsyncPlaceholder)) { if (isDef(vnode.asyncFactory.resolved)) { hydrate(oldVnode.elm, vnode, insertedVnodeQueue) } else { vnode.isAsyncPlaceholder = true } return } if (isTrue(vnode.isStatic) && isTrue(oldVnode.isStatic) && vnode.key === oldVnode.key && (isTrue(vnode.isCloned) || isTrue(vnode.isOnce)) ) { vnode.componentInstance = oldVnode.componentInstance return } let i const data = vnode.data if (isDef(data) && isDef(i = data.hook) && isDef(i = i.prepatch)) { i(oldVnode, vnode) } const oldCh = oldVnode.children const ch = vnode.children if (isDef(data) && isPatchable(vnode)) { for (i = 0; i < cbs.update.length; ++i) cbs.update[i](oldVnode, vnode) if (isDef(i = data.hook) && isDef(i = i.update)) i(oldVnode, vnode) } if (isUndef(vnode.text)) { if (isDef(oldCh) && isDef(ch)) { if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly) } else if (isDef(ch)) { if (isDef(oldVnode.text)) nodeOps.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)) { nodeOps.setTextContent(elm, '') } } else if (oldVnode.text !== vnode.text) { nodeOps.setTextContent(elm, vnode.text) } if (isDef(data)) { if (isDef(i = data.hook) && isDef(i = i.postpatch)) i(oldVnode, vnode) } }
const el = vnode.el = oldVnode.el This is a very important step. Let vnode.el refer to the current real dom. When el is modified, vnode.el will change synchronously.
Compare whether the two references are consistent
I don’t know what asyncFactory does after that, so I can’t understand this comparison
Static node comparison key, no re-rendering will be done after the same, directly copy componentInstance (once command takes effect here)
If vnode is a text node Or Comment node, but when vnode.text != oldVnode.text, you only need to update the text content of vnode.elm
Comparison of children
If only oldVnode has child nodes, then delete these nodes
If only vnode has child nodes, then delete them Create these child nodes. If oldVnode is a text node, set the text of vnode.elm to empty String
If both are present, updateChildren will be updated. This will be detailed later.
If neither oldVnode nor vnode has child nodes, but oldVnode is a text node or comment node, set the text of vnode.elm to an empty string
updateChildren
This part focuses on the entire algorithm
First four pointers, oldStart, oldEnd, newStart, newEnd, two arrays, oldVnode, Vnode .
function updateChildren (parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) { 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, vnodeToMove, refElm while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) { if (isUndef(oldStartVnode)) { oldStartVnode = oldCh[++oldStartIdx] // Vnode has been moved left } else if (isUndef(oldEndVnode)) { oldEndVnode = oldCh[--oldEndIdx] } else if (sameVnode(oldStartVnode, newStartVnode)) { patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue) oldStartVnode = oldCh[++oldStartIdx] newStartVnode = newCh[++newStartIdx] } else if (sameVnode(oldEndVnode, newEndVnode)) { patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue) oldEndVnode = oldCh[--oldEndIdx] newEndVnode = newCh[--newEndIdx] } else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue) canMove && nodeOps.insertBefore(parentElm, oldStartVnode.elm, nodeOps.nextSibling(oldEndVnode.elm)) oldStartVnode = oldCh[++oldStartIdx] newEndVnode = newCh[--newEndIdx] } else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode moved left patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue) canMove && nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm) oldEndVnode = oldCh[--oldEndIdx] newStartVnode = newCh[++newStartIdx] } else { if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx) idxInOld = isDef(newStartVnode.key) ? oldKeyToIdx[newStartVnode.key] : findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx) if (isUndef(idxInOld)) { // New element createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx) } else { vnodeToMove = oldCh[idxInOld] if (sameVnode(vnodeToMove, newStartVnode)) { patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue) oldCh[idxInOld] = undefined canMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm) } else { // same key but different element. treat as new element createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx) } } newStartVnode = newCh[++newStartIdx] } } if (oldStartIdx > oldEndIdx) { refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elm addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx, insertedVnodeQueue) } else if (newStartIdx > newEndIdx) { removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx) } }
Several situations and processing of a loop comparison (the following - all refer to index -) The comparison is the node node of the comparison. The abbreviation is not rigorous and the comparison uses the sameVnode function, which is not true. Congruent
Conditions for the entire loop not to end oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx
- ##oldStart === newStart, oldStart newStart
- oldEnd === newEnd, oldEnd-- newEnd--
- oldStart === newEnd, oldStart Insert to the end of the queue oldStart newEnd--
- oldEnd === newStart, oldEnd is inserted into the beginning of the queue oldEnd-- newStart
- This is the only way to handle all the remaining situations. kind of processing, after processing newStart
newStart在old中发现一样的那么将这个移动到oldStart前
没有发现一样的那么创建一个放到oldStart之前
循环结束后并没有完成
还有一段判断才算完
if (oldStartIdx > oldEndIdx) { refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elm addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx, insertedVnodeQueue) } else if (newStartIdx > newEndIdx) { removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx) }相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!
推荐阅读:
The above is the detailed content of Summary of diff algorithm usage skills. For more information, please follow other related articles on the PHP Chinese website!

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

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics



In the field of modern finance, with the rise of data science and artificial intelligence technology, quantitative finance has gradually become an increasingly important direction. As a statically typed programming language that can efficiently process data and deploy distributed systems, Go language has gradually attracted attention in the field of quantitative finance. This article will introduce how to use the Go language to perform quantitative financial analysis. The specific content is as follows: Obtaining financial data First, we need to obtain financial data. The network programming capabilities of Go language are very powerful and can be used to obtain various financial data. Compare

Summary of the system() function under Linux In the Linux system, the system() function is a very commonly used function, which can be used to execute command line commands. This article will introduce the system() function in detail and provide some specific code examples. 1. Basic usage of the system() function. The declaration of the system() function is as follows: intsystem(constchar*command); where the command parameter is a character.

With the rise of big data and data mining, more and more programming languages have begun to support data mining functions. As a fast, safe and efficient programming language, Go language can also be used for data mining. So, how to use Go language for data mining? Here are some important steps and techniques. Data Acquisition First, you need to obtain the data. This can be achieved through various means, such as crawling information on web pages, using APIs to obtain data, reading data from databases, etc. Go language comes with rich HTTP

How to use PHP to develop simple SEO optimization functions SEO (SearchEngineOptimization), or search engine optimization, refers to improving the website's ranking in search engines by improving the structure and content of the website, thereby obtaining more organic traffic. In website development, how to use PHP to implement simple SEO optimization functions? This article will introduce some commonly used SEO optimization techniques and specific code examples to help developers implement SEO optimization in PHP projects. 1. Friendly to use

How to use C# to write the minimum spanning tree algorithm. The minimum spanning tree algorithm is an important graph theory algorithm, which is used to solve the connectivity problem of graphs. In computer science, a minimum spanning tree refers to a spanning tree of a connected graph in which the sum of the weights of all edges of the spanning tree is the smallest. This article will introduce how to use C# to write the minimum spanning tree algorithm and provide specific code examples. First, we need to define a graph data structure to represent the problem. In C#, you can use an adjacency matrix to represent a graph. An adjacency matrix is a two-dimensional array in which each element represents

With the popularity of the Internet, more and more websites provide external link functions for pictures, videos and other resources. However, this external link function is easy to be stolen. Hotlinking means that other websites use pictures, videos and other resources on your website to directly display these resources on their own website through the reference address instead of downloading them to their own server. In this way, hotlink websites can use your website's traffic and bandwidth resources for free, which wastes resources and affects website speed. To address this problem, Nginx can be used to prevent hotlinking. Nginx is

Under Linux, it is very difficult to directly use the svndiff command to view code modifications, so I searched for a better solution on the Internet, which is to use vimdiff as a code viewing tool for svndiff, especially for those who are accustomed to using vim. It is very convenient. When using the svndiff command to compare the modifications of a certain file, for example, if you execute the following command: $svndiff-r4420ngx_http_limit_req_module.c, the following command will actually be sent to the default diff program: -u-Lngx_http_limit_req_module.c(revision4420)-Lngx_

One-click solution: Quickly master the usage skills of pip mirror source Introduction: pip is the most commonly used package management tool for Python, which can easily install, upgrade and manage Python packages. However, due to well-known reasons, using the default mirror source to download the installation package is slower. In order to solve this problem, we need to use a domestic mirror source. This article will introduce how to quickly master the usage skills of pip mirror source and provide specific code examples. Before you start, understand the concept of pip mirror source.
