Maison >interface Web >js tutoriel >Résumé des points de connaissance de l'algorithme diff de vue

Résumé des points de connaissance de l'algorithme diff de vue

亚连
亚连original
2018-05-28 11:26:331149parcourir

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, &#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 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.

  1. Comparez les deux références pour voir si elles sont cohérentes

  2. Je ne sais pas ce que fait asyncFactory après ça, donc je ne peux pas comprenez cela.

  3. 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)

  4. 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

  5. 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

  • Si oldVnode et vnode n'ont pas de nœuds enfants, mais que oldVnode est un nœud de texte ou un nœud de commentaire, définissez donc le texte de vnode.elm sur la chaîne vide

updateChildren

Cette partie se concentre toujours sur l'ensemble de l'algorithme

Quatre 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)
  }
 }
Plusieurs situations et traitement d'une comparaison de boucle (les ++ suivants -- font tous référence au ++ de l'index --) La comparaison est le nœud nœud comparé L'abréviation n'est pas rigoureuse et. la comparaison est utilisée La fonction sameVnode n'est pas vraiment congruente

La condition pour que la boucle entière ne se termine pas oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx

  1. oldStart === newStart, oldStart++ newEnd --

  2. oldEnd === newStart, oldEnd est inséré au début de la file d'attente oldEnd-- newStart++

  3. Toutes les situations restantes sont traitées de cette façon. Pour faire simple, il y a deux processus après traitement, newStart++

  4. newStart trouve le même dans old, puis. déplacez-le avant oldStart
  5. Si vous ne trouvez pas le même, créez-en un et placez-le avant oldStart
  • Il n'est pas terminé après la fin de la boucle

    Il y a encore une période de jugement avant qu'elle ne soit terminée
  • 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é.

  • Ce qui précède est ce que j'ai compilé pour vous, j'espère qu'il sera utile à tout le monde à l'avenir.

Articles associés :

Méthode Angular5 d'ajout de classes de style aux balises du composant lui-même
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)
  }

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!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn