Dieser Artikel gibt Ihnen eine Zusammenfassung relevanter Wissenspunkte über den Diff-Algorithmus von Vue. Interessierte Freunde können sich darauf beziehen.
Virtueller Dom
Der Diff-Algorithmus muss zunächst das Konzept klären, dass das Objekt von Diff der virtuelle Dom ist und die Aktualisierung des realen Doms das Ergebnis des Diff-Algorithmus ist
Vnode-Basisklasse
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 }
Dieser Teil des Codes dient hauptsächlich dazu, die Bedeutung bestimmter Diff-Attribute im Diff-Algorithmus besser zu verstehen Verstehen Sie die Vnode-Instanz
Der Gesamtprozess
Die Kernfunktion ist die Patch-Funktion
isUndef Beurteilung (ob es ist undefiniert oder null)
// leerer Mount (wahrscheinlich als Komponente), neues Root-Element erstellencreateElm(vnode, insertedVnodeQueue) Hier können Sie feststellen, dass die erstellten Knoten nicht einzeln eingefügt werden, aber in eine Warteschlange für die einheitliche Stapelverarbeitung stellen
Kernfunktion 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) ) ) ) }
Hier ist eine äußere Vergleichsfunktion, die die Schlüssel und Tags direkt vergleicht und Daten von zwei Knoten (Beachten Sie, dass sich die Daten hier auf VNodeData beziehen) und der Typ für die Eingabe direkt verglichen wird.
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; }
Dadurch wird bestätigt, ob die beiden Knoten einen weiteren Vergleichswert haben, andernfalls werden sie direkt ersetzt
Der Ersetzungsprozess ist hauptsächlich eine createElm-Funktion und die andere besteht darin, den oldVNode zu zerstören
// destroy old node if (isDef(parentElm)) { removeVnodes(parentElm, [oldVnode], 0, 0) } else if (isDef(oldVnode.tag)) { invokeDestroyHook(oldVnode) }
Einfügen Um den Prozess zu vereinfachen, müssen Sie den Typ des Knotens bestimmen und
createComponent aufrufen (es ermittelt, ob Kinder vorhanden sind, und ruft es dann rekursiv auf)
createComment
createTextNode
nach der Erstellung Nachdem Sie die Einfügefunktion
verwendet haben, müssen Sie die Hydrate-Funktion verwenden, um den virtuellen Dom und den realen Dom abzubilden
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) } } }
Kernfunktion
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 Dies ist ein sehr wichtiger Schritt. Lassen Sie vnode.el auf den aktuellen realen Dom verweisen, wenn el geändert wird. el wird sich synchron ändern.
Vergleichen Sie die beiden Referenzen, um zu sehen, ob sie konsistent sind
Ich weiß nicht, was asyncFactory danach macht, also kann ich es nicht Verstehe das.
Statischer Knotenvergleichsschlüssel, danach erfolgt kein erneutes Rendern, direkt kopieren ComponentInstance (sobald der Befehl hier wirksam wird)
Wenn vnode ein Textknoten oder Kommentarknoten ist, aber wenn vnode.text != oldVnode.text ist, müssen Sie nur den Textinhalt von vnode.elm aktualisieren
Vergleich von Kinder
Wenn nur oldVnode untergeordnete Knoten hat, dann löschen Sie diese Knoten
Wenn nur vnode untergeordnete Knoten hat, dann Erstellen Sie diese untergeordneten Knoten. Wenn oldVnode lautet: Der Textknoten setzt den Text von vnode.elm auf die leere Zeichenfolge
, dann wird updateChildren aktualisiert. Dies wird später detailliert beschrieben >
updateChildren
Der Fokus dieses Teils liegt immer noch auf dem gesamten AlgorithmusErste vier Zeiger, oldStart, oldEnd, newStart, newEnd, zwei 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) } }
Das ist es nicht abgeschlossen, nachdem die Schleife endet
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) }
Um es einfach auszudrücken: Schauen Sie sich nach dem Ende der Schleife den Inhalt zwischen den vier Zeigern an Das alte Array und das neue Array werden zurückgegeben und weniger hinzugefügt. Das Obige ist für alle zusammengestellt. Ich hoffe, dass es Ihnen in Zukunft nützlich sein wird.
Verwandte Artikel: Angular5-Methode zum Hinzufügen von Stilklassen zu den Tags der Komponente selbst vue-cli-Entwicklungsumgebung zur Implementierung Methode für domänenübergreifende Anfragen Detaillierte Erläuterung des Problems beim automatischen Build-Rem des Vue-cli-Webpack-MobilterminalsDas obige ist der detaillierte Inhalt vonZusammenfassung der Wissenspunkte zum Diff-Algorithmus von Vue. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!