Cet article partage avec vous un résumé des points de connaissances pertinents sur l'algorithme de comparaison de Vue. Les amis intéressés peuvent s'y référer.
Dom virtuel
L'algorithme de diff doit d'abord clarifier le concept selon lequel l'objet de diff est le dom virtuel, et la mise à jour du dom réel est le résultat de l'algorithme de diff
Classe de base Vnode
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 }
Cette partie du code vise principalement à mieux comprendre la signification des attributs diff spécifiques dans l'algorithme diff, et bien sûr à mieux comprendre l'instance vnode
Le processus global
La fonction principale est la fonction patch
est un jugement Undef (que ce soit est indéfini ou nul)
// montage vide (probablement en tant que composant), créez un nouvel élément racinecreateElm(vnode, insertVnodeQueue) Ici vous pouvez constater que la création de nœuds n'est pas insérée un par un, mais mis dans une file d'attente pour un traitement par lots unifié
Fonction principale 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) ) ) ) }
Voici une fonction de comparaison externe qui compare directement les clés et les balises de deux nœuds Comparaison des données (notez que les données font ici référence à VNodeData), en cas d'entrée, comparez directement le type.
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; }
Cela confirmera si les deux nœuds méritent une comparaison plus approfondie, sinon ils seront remplacés directement
Le processus de remplacement est principalement une fonction createElm et l'autre est la destruction de oldVNode
// destroy old node if (isDef(parentElm)) { removeVnodes(parentElm, [oldVnode], 0, 0) } else if (isDef(oldVnode.tag)) { invokeDestroyHook(oldVnode) }
Le processus d'insertion consiste simplement à déterminer le type du nœud et à appeler
createComponent (il déterminera s'il y a des enfants puis l'appellera de manière récursive)
createComment
createTextNode
Utilisez la fonction d'insertion après la création
Après cela, vous devez utiliser la fonction hydrate pour mapper le dom virtuel et le dom réel
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) } } }
Fonction principale
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 C'est une étape très importante Laissons vnode.el faire référence au domaine réel actuel. el est modifié, vnode.el changera de manière synchrone.
Comparez les deux références pour voir si elles sont cohérentes
Je ne sais pas ce que fait asyncFactory après ça, donc je ne peux pas comprenez cela.
Clé de comparaison de nœuds statiques, aucun nouveau rendu ne sera effectué après le même, copiez directement l'instance de composant (une fois que la commande prend effet ici)
Si vnode est un nœud de texte ou un nœud de commentaire, mais lorsque vnode.text != oldVnode.text, il vous suffit de mettre à jour le contenu texte de vnode.elm
Comparaison de enfants
Si seul oldVnode a des nœuds enfants, supprimez ces nœuds
Si seul vnode a des nœuds enfants, alors créez ces nœuds enfants. Ici, si oldVnode est Le nœud de texte définit le texte de vnode.elm sur la chaîne vide
, alors updateChildren sera mis à jour. Cela sera détaillé plus tard<🎜. >
updateChildren
Cette partie se concentre toujours sur l'ensemble de l'algorithmeQuatre premiers pointeurs, oldStart, oldEnd, newStart, newEnd, deux tableaux, oldVnode, Nœud virtuel.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) } }
Pour faire simple, une fois la boucle terminée, regardez le contenu entre les quatre pointeurs In. l'ancien tableau et le nouveau tableau, plus est renvoyé et moins est ajouté.
Articles associés :
Méthode Angular5 d'ajout de classes de style aux balises du composant lui-mêmeif (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) }
Environnement de développement vue-cli à implémenter Méthode de requêtes inter-domaines
Explication détaillée du problème de construction automatique du terminal mobile Vue-cli webpack
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!