[Algoritmo] Algoritmo de diferencia

Diferencia tres estrategias
La función del algoritmo Diff es calcular la parte modificada del DOM virtual y luego realizar operaciones DOM nativas en esta parte sin volver a renderizar la página completa.
El algoritmo Diff tiene tres estrategias:

  • Diferencia de árbol
  • Diferencia de componente
  • Diferencia de elemento

El orden de ejecución de las tres estrategias también se ejecuta de forma secuencial.
Tree Diff atraviesa cada capa del árbol para encontrar la diferencia, como se muestra en la Figura 1. Diferencia de árbol
Component Diff es una comparación de diferencias a nivel de datos

  1. Si son todos componentes del mismo tipo (es decir, dos nodos son dos instancias diferentes de la misma clase de componente, por ejemplo:
    versus
    ), continúe comparando el árbol DOM virtual de acuerdo con la estrategia original
  2. Si hay un componente que no es del mismo tipo, el componente se juzga como un componente sucio, para reemplazar todos los nodos secundarios debajo del componente completo.

Representación de DOM real de Element Diff , comparación de diferencias estructurales

Primero compare la primera capa, la primera capa es toda R, sin cambios; luego ingrese la segunda capa Component Diff, encuentre que el componente A no lo es, elimine A y sus subcomponentes B, C; finalmente compare la tercera capa, cree A Y sus subcomponentes B y C.

Cuando los nodos están en el mismo nivel, Diff proporciona tres operaciones DOM: eliminar, mover e insertar .

Diferencia de elemento
Como se muestra en la Figura 2, la primera y última posición de OldVnode y NewVnode están marcadas como oldS, oldE, newS y newE, respectivamente.

(1) oldS y newS son iguales, sin cambios, oldS ++, newS ++.

oldS = a,oldE = d
newS = a, newE = c

(2) NewS no coincide con OldVnode, inserte f, newS ++ antes de oldS.

oldS = b,oldE = d
newS = f, newE = c

(3) newS es lo mismo que oldE, oldE se mueve al frente de oldS, newS ++, oldE–.

oldS = b,oldE = d
newS = d, newE = c

(4) newE es lo mismo que oldE, no se produce ningún cambio, newE–, oldE–.

oldS = b,oldE = c
newS = e, newE = c

(5) No es lo mismo, inserte newE antes de oldS, elimine oldS, oldS ++, newS ++, newE–, oldE–.

oldS = b,oldE = b
newS = e, newE = e

Finalmente, adjunte el análisis del código fuente central:
parche

function patch (oldVnode, vnode) {
    
    
    // some code
    if (sameVnode(oldVnode, vnode)) {
    
    
        patchVnode(oldVnode, vnode)
    } else {
    
    
        const oEl = oldVnode.el // 当前oldVnode对应的真实元素节点
        let parentEle = api.parentNode(oEl)  // 父元素
        createEle(vnode)  // 根据Vnode生成新元素
        if (parentEle !== null) {
    
    
            api.insertBefore(parentEle, vnode.el, api.nextSibling(oEl)) // 将新元素添加进父元素
            api.removeChild(parentEle, oldVnode.el)  // 移除以前的旧元素节点
            oldVnode = null
        }
    }
    // some code 
    return vnode
}

patchVnode (oldVnode, vnode) {
    
    
    const el = vnode.el = oldVnode.el
    let i, oldCh = oldVnode.children, ch = vnode.children
    if (oldVnode === vnode) return
    if (oldVnode.text !== null && vnode.text !== null && oldVnode.text !== vnode.text) {
    
    
        api.setTextContent(el, vnode.text)
    }else {
    
    
        updateEle(el, vnode, oldVnode)
        if (oldCh && ch && oldCh !== ch) {
    
    
            updateChildren(el, oldCh, ch)
        }else if (ch){
    
    
            createEle(vnode) //create el's children dom
        }else if (oldCh){
    
    
            api.removeChildren(el)
        }
    }
}

Esta función hace lo siguiente:

  • Encuentre el dom real correspondiente, llamado el
  • Juzgue si Vnode y oldVnode apuntan al mismo objeto, si es así, regrese directamente
  • Si ambos tienen nodos de texto y no son iguales, establezca el nodo de texto de el en el nodo de texto de Vnode.
  • Si oldVnode tiene nodos secundarios pero Vnode no, elimine los nodos secundarios de el
  • Si oldVnode no tiene nodos secundarios pero Vnode sí, agregue los nodos secundarios de Vnode a el después de darse cuenta
  • Si ambos tienen nodos secundarios, ejecute la función updateChildren para comparar los nodos secundarios

updateChildren

function updateChildren (parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) {
    
    
    var oldStartIdx = 0;
    var newStartIdx = 0;
    var oldEndIdx = oldCh.length - 1;
    var oldStartVnode = oldCh[0];
    var oldEndVnode = oldCh[oldEndIdx];
    var newEndIdx = newCh.length - 1;
    var newStartVnode = newCh[0];
    var newEndVnode = newCh[newEndIdx];
    var oldKeyToIdx, idxInOld, vnodeToMove, refElm;

    var canMove = !removeOnly;
    {
    
    
        checkDuplicateKeys(newCh);
    }
    // oldVnode起始位置小于结束位置并且newVnode起始位置小于结束位置
    while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
    
    
        // isUndef 用来判断对象是否等于undefined或者为空,是的话返回true
        if (isUndef(oldStartVnode)) {
    
    
            // oldVnode 起始位置oldS++
            oldStartVnode = oldCh[++oldStartIdx]; // Vnode has been moved left
        } else if (isUndef(oldEndVnode)) {
    
    
            // oldVnode 结束位置oldE--
            oldEndVnode = oldCh[--oldEndIdx];
        } else if (sameVnode(oldStartVnode, newStartVnode)) {
    
    
            // oldS和newS相同,不变化,进行patch,oldS++,newS++
            patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx);
            oldStartVnode = oldCh[++oldStartIdx];
            newStartVnode = newCh[++newStartIdx];
        } else if (sameVnode(oldEndVnode, newEndVnode)) {
    
    
            // oldE和newE相同,不变化,进行patch,oldE--,newE--
            patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx);
            oldEndVnode = oldCh[--oldEndIdx];
            newEndVnode = newCh[--newEndIdx];
        } else if (sameVnode(oldStartVnode, newEndVnode)) {
    
     // Vnode moved right
            // oldS和newE相同,oldS移动到oldE之后,进行patch,oldS++,newE--
            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
            // oldE和newS相同,oldE移动到oldS之前,进行patch,oldE--,newS++
            patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx);
            canMove && nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm);
            oldEndVnode = oldCh[--oldEndIdx];
            newStartVnode = newCh[++newStartIdx];
        } else {
    
    
            // 全都不相同情况下
            // 获取oldVnode->index的key
            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
                // oldVnode->index为undefined或null,说明没有该元素,创建新的元素
                createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx);
            } else {
    
    
                  // 获取oldVnode
                  vnodeToMove = oldCh[idxInOld];
                  if (sameVnode(vnodeToMove, newStartVnode)) {
    
    
                      // 创建的Vnode和newS相同,插入到oldS之前,进行patch
                      patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue, newCh, newStartIdx);
                      oldCh[idxInOld] = undefined;
                      canMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm);
                  } else {
    
    
                      // 相同的key但是不一样的element. 被视为新的element
                      createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx);
                  }
            }
            newStartVnode = newCh[++newStartIdx];
        }
    }
    // 当oldS>oldE时,将newS至newE间的全部插入
    if (oldStartIdx > oldEndIdx) {
    
    
        refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elm;
        addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx, insertedVnodeQueue);
    } else if (newStartIdx > newEndIdx) {
    
    
        // 当newS>newE,将oldS至oldE间的全部删除
        removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx);
    }
}

Supongo que te gusta

Origin blog.csdn.net/u013034585/article/details/106128563
Recomendado
Clasificación