Heim > Artikel > Web-Frontend > Eingehende Analyse des Prinzips des Diff-Algorithmus in vue2.x
Der Diff-Algorithmus ist ein effizienter Algorithmus, der Baumknoten auf derselben Ebene vergleicht und so die Notwendigkeit vermeidet, den Baum Schicht für Schicht zu durchsuchen und zu durchlaufen. Dieser Artikel bietet Ihnen eine ausführliche Analyse der Prinzipien des Diff-Algorithmus in vue2.x. Ich hoffe, er wird Ihnen hilfreich sein!
Ich habe viele Artikel zur Quellcode-Analyse gelesen und den Quellcode mindestens zweimal gelesen. Schließlich möchte ich es immer noch selbst schreiben, als eine Art Aufzeichnung und zum Selbststudium. Konzentrieren Sie sich auf die Kommentare und die Zusammenfassung, machen Sie sich nicht zu viele Sorgen um den Rest. Sie werden mehr gewinnen, wenn Sie den Prozess und die Kommentare überprüfen, indem Sie die Zusammenfassung mit dem Quellcode vergleichen.
Nachdem die Renderfunktion generiert wurde, wird die Diff-Berechnung während der Initialisierung durchgeführt, da kein alter virtueller Knoten vorhanden ist Da es sich bei dem virtuellen Knoten nicht um einen nativen Knoten handelt, wird zum ersten Mal ein neuer Ersatzvorgang durchgeführt. (Teilen von Lernvideos: vue-Video-Tutorial)
// /src/core/instance/lifecycle.js Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) { const vm: Component = this const prevEl = vm.$el const prevVnode = vm._vnode const restoreActiveInstance = setActiveInstance(vm) vm._vnode = vnode // 当前render函数产生的虚拟节点,保存后以便下次做对比 if (!prevVnode) { vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false) //初次渲染 } else { vm.$el = vm.__patch__(prevVnode, vnode) } ... }
Der Hauptteil besteht aus zwei Hauptzweigen: Die vorderen und hinteren virtuellen Knoten sind konsistent, und die vorderen und hinteren virtuellen Knoten sind konsistent inkonsistent
// /src/core/vdom/patch.js export function createPatchFunction (backend) { ... return function patch (oldVnode, vnode, hydrating, removeOnly) { ... if (!isRealElement && sameVnode(oldVnode, vnode)) { ...// 前后虚拟节点一致的方法 } else { ...// 前后虚拟节点不一致的方法 } } }
Es ist in drei Schritte unterteilt: 1. Erstellen Sie einen neuen Knoten, 2. Aktualisieren Sie den übergeordneten Platzhalterknoten, 3. Löschen Sie den alten Knoten
Die beiden sind unterschiedlich Die Komponente wird zum ersten Mal gemountet. Anschließend wird beurteilt, ob es sich um einen echten Dom handelt. Er wird in einen virtuellen Knoten umgewandelt und ersetzt. Die virtuellen Knoten davor und danach sind konsistent. Bestimmen Sie zuerst Wenn ja, legen Sie den Text direkt fest. Wenn nicht, fahren Sie mit der Bestimmung fort
if (isRealElement) { ... //需要diff 所以将第一次的真实节点转换成虚拟节点 oldVnode = emptyNodeAt(oldVnode) //<div id="app"></div> } // 拿到父类的dom节点 const oldElm = oldVnode.elm //app const parentElm = nodeOps.parentNode(oldElm) // body //创建新dom节点 内部包含组件逻辑 createElm( vnode, insertedVnodeQueue, oldElm._leaveCb ? null : parentElm, nodeOps.nextSibling(oldElm) ) //更新父的占位符节点 (组件更新相关) if (isDef(vnode.parent)) { // 在生成render函数时会生成占位符节点<Dialog>提示</Dialog> => <div>提示</div> <Dialog></Dialog>就是占位符节点 let ancestor = vnode.parent // 判断是否可挂载 const patchable = isPatchable(vnode) while (ancestor) { for (let i = 0; i < cbs.destroy.length; ++i) { cbs.destroy[i](ancestor) } //更新父占位符的element ancestor.elm = vnode.elm if (patchable) { ... } else { registerRef(ancestor) } ancestor = ancestor.parent } } // 删除旧节点 if (isDef(parentElm)) { removeVnodes([oldVnode], 0, 0) } else if (isDef(oldVnode.tag)) { invokeDestroyHook(oldVnode) }
function patchVnode (oldVnode,vnode,insertedVnodeQueue,ownerArray,index,removeOnly) { const elm = vnode.elm = oldVnode.elm let i const data = vnode.data // 是组件vnode,在组件更新会调用组件的prepatch方法 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) } // 是否是text if (isUndef(vnode.text)) { // 新旧节点都有children if (isDef(oldCh) && isDef(ch)) { if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly) // 新有 老没有 children 循环创建新节点 } else if (isDef(ch)) { if (isDef(oldVnode.text)) nodeOps.setTextContent(elm, '') addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue) // 新没有 老有 children 直接删除老节点 } else if (isDef(oldCh)) { removeVnodes(oldCh, 0, oldCh.length - 1) // 新老都没有 children 老的是文本 就置为空 } else if (isDef(oldVnode.text)) { nodeOps.setTextContent(elm, '') } // 是text 直接设置文本 } 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) } }
In einigen Fällen kann der Index nicht als Schlüssel verwendet werden, da der Index vor und nach der Änderung derselbe ist. Wenn der Index als Schlüssel verwendet wird, tritt ein Aktualisierungsfehler auf Ich werde es so verstehen, dass am Ende ein Element hinzugefügt wird (da die Schlüssel davor und danach alle 0 sind). Vergleiche, die Rolle des Schlüssels ist groß. Ohne Schlüssel treten Aktualisierungsfehler auf und der Wiederverwendungseffekt wird nicht erreicht.
Tiefe zuerst, Vergleich derselben EbenepatchVnode
für einen umfassenden Vergleich auf und verschieben Sie dann den Index zum nächsten ElementpatchVnode
方法进行深层对比,之后移动索引至下一个元素patchVnode
方法进行深层对比,之后移动索引至上一个元素patchVnode
方法进行深层对比,之后将旧节点元素移动至最后,旧节点头指针移动到下一个,新节点的尾指针移动至上一个。例如旧:A,B,C,新:C,B,A,第一次对比将旧A移动到C后边patchVnode
patchVnode
für einen tiefen Vergleich auf und verschieben Sie dann den Index zum vorherigen ElementpatchVnode
rekursiv auf. > Die Methode führt einen tiefen Vergleich durch und verschiebt dann das alte Knotenelement an das Ende den Kopfzeiger des alten Knotens auf den nächsten und verschiebt den Schwanzzeiger des neuen Knotens auf den vorherigen. Zum Beispiel alt: A, B, C, neu: C, B, A. Verschieben Sie für den ersten Vergleich das alte A an die Rückseite von CSchwanz- und KopfvergleichVergleichen Sie das Schwanzelement des alten Knotens mit dem Wenn sie gleich sind, rufen Sie die Methode patchVnode
für einen tiefen Vergleich auf und verschieben Sie dann das alte Knotenelement nach vorne, wobei der Endzeiger des alten Knotens auf den vorherigen verweist. und der Kopfzeiger des neuen Knotens auf den nächsten. Zum Beispiel alt: A, B, C, neu: C, B, A, D. Beim ersten Vergleich wird das alte C an den Anfang von A verschoben.Vergleich außerhalb der Reihenfolge
Das obige ist der detaillierte Inhalt vonEingehende Analyse des Prinzips des Diff-Algorithmus in vue2.x. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!