Le DOM-Diff Vue

VNODE

Type Vue VNODE utilisé pour sauver dom virtuel, Vue correspondant au code source de /* src/core/vdom/vnode.js */définition de fichier. Différents types de noeuds peuvent être décrits par un ensemble de propriétés de colocalisation

    this.tag = tag  /* el标签名 */
    this.data = data  /* 存储特性信息 */
    this.children = children  /*当前节点的子节点,是一个数组*/
    this.text = text    /*  当前节点的文本  */
    this.elm = elm /*当前虚拟节点对应的真实dom节点*/
    this.ns = undefined
    this.context = context // 当前节点对应的Vue实例
    this.fnContext = undefined  /* 函数组件对应的option实例 */
    this.fnOptions = undefined /* 函数式组件对应的option选项  */
    this.fnScopeId = undefined 
    this.key = data && data.key  /*节点的key属性,被当作节点的标志,用以优化*/
    this.componentOptions = componentOptions /* 组件的option选项 */
    this.componentInstance = undefined /*当前节点对应的组件的实例*/
    this.parent = undefined /* 当前节点父节点 */
    this.raw = false
    this.isStatic = false /*  是否为静态节点  类似<p>静态节点,不包括数据</p>*/ 
    this.isRootInsert = true
    this.isComment = false /* 是否为注释节点 */
    this.isCloned = false
    this.isOnce = false /* 是否具有v-once 指令*/
    this.asyncFactory = asyncFactory
    this.asyncMeta = undefined
    this.isAsyncPlaceholder = false

Par exemple, pour créer un noeud de commentaire

//  创建注释节点
export const createEmptyVNode = (text: string = '') => {
  const node = new VNode()
  // 注释描述
  node.text = text
  // 是注释
  node.isComment = true
  return node
}

DOM-DIFF

DOM-Diff est en fait une comparaison des anciens et nouveaux VNODE bien sûr, étaient les anciens noeuds add VNODE noeuds de suppression, les noeuds processus de mise à jour. Ce processus semble se référer au processus VNODE nouveau patch (patch) de l'ancien VNODE, la procédure définie dans le code source src\core\vdom\patch.jsdans.

  1. nœuds anciens et nouveaux sont des noeuds statiques (similaires <p>不存在</p>) et la même clé pour la sortie
    // 新节点和就节点都是静态节点则退出
    if (isTrue(vnode.isStatic) &&
      isTrue(oldVnode.isStatic) &&
      vnode.key === oldVnode.key &&
      (isTrue(vnode.isCloned) || isTrue(vnode.isOnce))
    ) {
      vnode.componentInstance = oldVnode.componentInstance
      return
    }
  1. Si le nouveau VNODE pas d'attributs de texte
    1. Deux nœuds ont des nœuds enfants du nœud enfant et mise à jour inégale
    2. New VNODE a des nœuds enfants, le vieux VNODE pas d'enfants, par rapport aux anciens noeuds add VNODE
    3. Old VNODE ont des nœuds enfants, nouveau VNODE pas, supprimer les anciens nœuds enfants de VNODE
    4. Ne répond pas à ce qui précède, et l'ancien VNODE le texte, puis supprimez l'ancien texte VNODE
 // 有没有text属性
    if (isUndef(vnode.text)) {
      // 如果两个都有子节点
      if (isDef(oldCh) && isDef(ch)) {
        // 如果不相等则更新子节点
        if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly)
        
      } 
      // 旧节点没有子节点,新节点有
      else if (isDef(ch)) {
        if (process.env.NODE_ENV !== 'production') {
          checkDuplicateKeys(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, '')
      }
    } 
  1. S'il y a un nouveau attributs de texte VNODE, encore plus ancien et le nouveau texte VNODE pour le nouveau texte VNODE
    // 旧节点的文本不等于新节点
    else if (oldVnode.text !== vnode.text) {
      nodeOps.setTextContent(elm, vnode.text)
    }

Mise à jour de nœuds enfants

Le ci-dessus DOM-Diff est en fait très bonne compréhension, le lieu peut être un peu plus difficile à traiter, il est neuf, vieux VNODE ont des nœuds enfants doivent traverser les nouveaux, les anciens nœuds enfants VNODE être mis à jour.

  // 如果两个都有子节点
      if (isDef(oldCh) && isDef(ch)) {
        // 如果不相等则更新子节点
        if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly)   
      } 

stratégie de mise à jour qui est la boucle externe newChildren, les oldChildren de boucle interne. La boucle principale quand si peu de cas

  1. Pour la première fois trouvé la même mise à jour de patch noeud noeud
  2. Il trouve être identiques mais différents des positions de noeuds et le noeud sera déplacé vers le patch avant que tous les nœuds en suspens, et non après avoir été déplacé vers un noeud de traitement, si le nœud existe parce que ajouté avant, cela devrait être déplacé vers le noeud courant après le nouveau nœud avant, avant le nouvel endroit où il occupera après le nœud si déplacé vers les nœuds de traitement
  3. Si le précédent noeud correspondant ne se trouve pas dans oldChildren, puis ajouter un nœud à tous les nœuds en cours
  4. Si traversé newChildren, oldChildren il y a un noeud, puis supprimez les nœuds restants
 if (isUndef(idxInOld)) {   // 如果在oldChildren里找不到当前循环的newChildren里的子节点
          // 那么久创建子节点并插入到合适位置
          createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)
        }
        // 在old中找到newchild子节点 
        else {
          // 需要移动的子节点
          vnodeToMove = oldCh[idxInOld]
          // 如果两个子节点相同
          if (sameVnode(vnodeToMove, newStartVnode)) {
            // 更新子节点
            patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)
            oldCh[idxInOld] = undefined
            // canmove表示需要移动节点
            canMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm)
          } 
          else {
            // 相同接key,但是是不同的元素
            createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)
          }
        }

Optimisation mettre à jour le nœud enfant

Boucle à travers l'enfant noeuds nouvelle méthode, même si elle peut répondre aux exigences, mais si cela est un cycle direct, la quantité des données de nœud enfant relativement longue, la complexité de temps sera élevé, alors vous devez mettre à jour le nœud enfant afin d' optimiser l'utilisation des nouvelles NEWSTART Front nouveau poste newEnd, vieux avant oldStart, après oldEnd avant que l'ancienne comparaison dans les deux sens, la comparaison entre deux
Insérer ici l'image Description

  1. Si la même comparaison avant la nouvelle mise à jour et le patch avant que l'ancien, alors que le nouveau et l'ancien ancien indice avant l'augmentation de l'indice
  2. Après les anciens et nouveaux sont mises à jour égales de patch, tandis que l'ancien index et la nouvelle réduction
  3. La même avant et après le nouveau patch pour mettre à jour l'ancienne, avant de passer à noeud tous les noeuds en cours, alors que le nouvel indice avant l'augmentation, après l'ancien indice baisse
  4. Après le nouveau patch est la même qu'avant la mise à jour et vieux, après le nœud mobile à tous les nœuds non traités, alors que la nouvelle réduction de l'indice, avant que l'ancien index par
  5. Si cela ne se fait pas au-dessus de la mise à jour du cycle ordinaire
  6. Si oldStartIdx Index> Index oldEndIdx représente le noeud newChildren n'a pas encore terminé le cycle, est ajouté (newStartIdx, newEndIdx) Fin plage des nœuds du DOM
  7. Si newStartIdx Index> Index newEndIdx, il n'y a pas oldChildren parcouraient, puis retirez le (oldStartIdx, oldEndIdx) noeud de plage

Il convient de noter que la mise à jour sur tous les noeuds sont effectués pour l'ancien VNODE, c'est-à-dire en face des noeuds du processus de patch

    // 新前、新后,旧前、旧后两两更新
    while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
      // 如果子节点没有,就下一个
      if (isUndef(oldStartVnode)) {
        oldStartVnode = oldCh[++oldStartIdx] // Vnode has been moved left
      }
      // 如果end不存在 则向前 
      else if (isUndef(oldEndVnode)) {
        oldEndVnode = oldCh[--oldEndIdx]
      }
      // 如果新前节点与旧前节点相同,则更新,并同时向后移 
      else if (sameVnode(oldStartVnode, newStartVnode)) {
        patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)
        oldStartVnode = oldCh[++oldStartIdx]
        newStartVnode = newCh[++newStartIdx]
      }
      // 如果旧后与新后相同,则更新,并同时向前移
      else if (sameVnode(oldEndVnode, newEndVnode)) {
        patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx)
        oldEndVnode = oldCh[--oldEndIdx]
        newEndVnode = newCh[--newEndIdx]
      }
      // 如果旧前与新后相同则更新并移动 
      else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right
        patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx)
        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, newCh, newStartIdx)
        canMove && nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm)
        oldEndVnode = oldCh[--oldEndIdx]
        newStartVnode = newCh[++newStartIdx]
      }
      //  如果不是上述情况将就进行普通的循环对比
      else {
        // 如果在oldChildren里找不到当前循环的newChildren里的子节点
        if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)
        idxInOld = isDef(newStartVnode.key)
          ? oldKeyToIdx[newStartVnode.key]
          : findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)
        if (isUndef(idxInOld)) {   // 如果在oldChildren里找不到当前循环的newChildren里的子节点
          // 那么久创建子节点并插入到合适位置
          createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)
        }
        // 在old中找到newchild子节点 
        else {
          // 需要移动的子节点
          vnodeToMove = oldCh[idxInOld]
          // 如果两个子节点相同
          if (sameVnode(vnodeToMove, newStartVnode)) {
            // 更新子节点
            patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)
            oldCh[idxInOld] = undefined
            // canmove表示需要移动节点
            canMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm)
          } 
          else {
            // 相同接key,但是是不同的元素
            createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)
          }
        }
        newStartVnode = newCh[++newStartIdx]
      }
    }
    // old循环完,new没循环完,将newStart -> newEnd 中间的值插入到DOM中
    if (oldStartIdx > oldEndIdx) {
      refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elm
      addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx, insertedVnodeQueue)
    }
    // 如果新节点遍历完,old还没有遍历完,将oldStart -> oldEnd 的DOM移除
     else if (newStartIdx > newEndIdx) {
      removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx)
    }
Publié 85 articles originaux · a gagné les éloges 62 · vues 20000 +

Je suppose que tu aimes

Origine blog.csdn.net/qq_36754767/article/details/104374510
conseillé
Classement