Heim  >  Artikel  >  Web-Frontend  >  So verwenden Sie den Diff-Algorithmus in Vue

So verwenden Sie den Diff-Algorithmus in Vue

php中世界最好的语言
php中世界最好的语言Original
2018-05-26 10:44:011761Durchsuche

Dieses Mal zeige ich Ihnen, wie Sie den Diff-Algorithmus in Vue verwenden und welche Vorsichtsmaßnahmen bei der Verwendung des Diff-Algorithmus in Vue gelten. Hier ist ein praktischer Fall, schauen wir uns das an.

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, &#39;&#39;)
    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, &#39;&#39;)
   }
  } 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, wenn el auf den aktuellen realen Dom verweist , vnode.el wird sich synchron ändern.

  1. Vergleichen Sie, ob die beiden Referenzen konsistent sind

  2. Ich weiß nicht, was asyncFactory danach macht, also kann ich es nicht Verstehen Sie diesen Vergleich

  3. Statischer Knotenvergleichsschlüssel, danach wird kein erneutes Rendern durchgeführt, kopieren Sie direkt die KomponenteInstanz (sobald der Befehl hier wirksam wird)

  4. Wenn vnode ein Textknoten oder ein Kommentarknoten ist, aber wenn vnode.text != oldVnode.text ist, müssen Sie nur den Textinhalt von vnode.elm

  5. <🎜 aktualisieren >
  6. Vergleich der untergeordneten Knoten

  • Wenn nur oldVnode untergeordnete Knoten hat, dann

    löschen Sie diese Knoten

  • Wenn nur vnode vorhanden ist, erstellen Sie diese untergeordneten Knoten. Wenn oldVnode ein Textknoten ist, setzen Sie den Text von vnode.elm auf eine leere

    Zeichenfolge

  • beide updateChildren, dies wird später detailliert beschrieben

  • Wenn weder oldVnode noch vnode untergeordnete Knoten haben, oldVnode jedoch ein Textknoten oder Kommentarknoten ist, legen Sie den Text von vnode fest .elm in einen leeren String

updateChildren

Der Fokus dieses Teils liegt immer noch auf dem gesamten Algorithmus

Die ersten 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)
  }
 }
Eine

Schleife Mehrere Situationen und Verarbeitungsvergleiche (die folgenden ++ -- alle beziehen sich auf den ++ des Index --) Der Vergleich ist der Knotenknoten des Vergleichs, der Abkürzung ist nicht streng. Die zum Vergleich verwendete Funktion sameVnode ist nicht wirklich kongruent

Die Bedingung dafür, dass die gesamte Schleife nicht endet oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx

  1. oldStart == = newStart, oldStart++ newStart++

  2. oldEnd === newEnd, oldEnd-- newEnd--

  3. oldStart === newEnd, oldStart eingefügt in das Ende des Teams oldStart++ newEnd--

  4. oldEnd === newStart, oldEnd wird in den Anfang des Teams oldEnd-- newStart++

    eingefügt

  5. 剩下的所有情况都走这个处理简单的说也就两种处理,处理后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中文网其它相关文章!

推荐阅读:

怎样使用angular4在多个组件中数据通信

使用Vue单页应用时应该如何引用单独样式文件

Das obige ist der detaillierte Inhalt vonSo verwenden Sie den Diff-Algorithmus in Vue. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn