The core use

  • Solves row sorting for the most frequently updated tables

The key to improving performance

  • Since dom creation is expensive, it’s better to use the original DOM if you can, using node.insertBefore () instead of createElement+replaceChild

Algorithm analysis

  1. First go order:
    1. oldStartVnode, newStartVnode
    2. oldEndVnode, newEndVnode
    3. oldStartVnode, newEndVnode
    4. oldEndVnode, newStartVnode
  2. Failing all of the above,
    1. FindIndex, walk through the Vnode to see if it is the same Vnode
      1. Although it is one by one traversal, but the speed of JS is very fast, compared to the consumption of dom operation, or very cheap
    2. So I’m going to look for the key first, and if I have the key, I’m going to use the map that I’m going to map with the key.
    3. Therefore, if there is frequent commutator, it is necessary to add a key, such as v-for, which can improve the performance of diff
      1. How it works: insertBefore definitely performs better than createElement+replaceChild
  3. The following is the diff algorithm core source code, with personal comments
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;

    // removeOnly is a special flag used only by <transition-group>
    // to ensure removed elements stay in correct relative positions
    // during leaving transitions
    varcanMove = ! removeOnly; { checkDuplicateKeys(newCh); }while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
        if (isUndef(oldStartVnode)) { // Make sure oldVnode exists (this function handles child, here it determines whether oldVnode has child)
            oldStartVnode = oldCh[++oldStartIdx]; // Vnode has been moved left
        } else if (isUndef(oldEndVnode)) { // Make sure oldvNodes exist
            oldEndVnode = oldCh[--oldEndIdx];
        } else if (sameVnode(oldStartVnode, newStartVnode)) { // (compare from top to bottom) If the old vNode is the same as the new vnode, then run patchVnode recursively
            patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx); // After running a child, run the next child
            oldStartVnode = oldCh[++oldStartIdx];
            newStartVnode = newCh[++newStartIdx];
        } else if (sameVnode(oldEndVnode, newEndVnode)) { // (from bottom to top)
            patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx);
            oldEndVnode = oldCh[--oldEndIdx];
            newEndVnode = newCh[--newEndIdx];
        } else if (sameVnode(oldStartVnode, newEndVnode)) { // (The scenario table is reordered by the index in reverse order) Vnode Moved Right
            patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx);
            canMove && nodeOps.insertBefore(parentElm, oldStartVnode.elm, nodeOps.nextSibling(oldEndVnode.elm)); // Insert the oldNode DOM directly into the new ELM because of sameNode
            oldStartVnode = oldCh[++oldStartIdx];
            newEndVnode = newCh[--newEndIdx];
        } else if (sameVnode(oldEndVnode, newStartVnode)) { // (Reverse order) Vnode Moved left
            patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx);
            canMove && nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm); // Insert the oldNode DOM directly into the new ELM because of sameNode
            oldEndVnode = oldCh[--oldEndIdx];
            newStartVnode = newCh[++newStartIdx];
        } else {
            if (isUndef(oldKeyToIdx)) { oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx); }
            idxInOld = isDef(newStartVnode.key) // If there is a key in the patchVnode list, then run patchVnode again. If there is a key in the patchVnode list, run patchVnode again
                ? 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, newCh, newStartIdx);
                    oldCh[idxInOld] = undefined;
                    canMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm); // Insert the oldNode DOM directly into the new ELM because of sameNode
                } 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) { // (after comparing sameVnode)newVnode is more than oldVnode
        refElm = isUndef(newCh[newEndIdx + 1])?null : newCh[newEndIdx + 1].elm;
        addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx, insertedVnodeQueue);
    } else if (newStartIdx > newEndIdx) { If newVnode is less than oldVnode, delete some nodes from the original pageremoveVnodes(oldCh, oldStartIdx, oldEndIdx); }}function checkDuplicateKeys (children) {
    var seenKeys = {};
    for (var i = 0; i < children.length; i++) {
        var vnode = children[i];
        var key = vnode.key;
        if (isDef(key)) {
            if (seenKeys[key]) {
                warn(
                    ("Duplicate keys detected: '" + key + "'. This may cause an update error."),
                    vnode.context
                );
            } else {
                seenKeys[key] = true; }}}}function sameVnode (a, b) { // Judge rule: 1. Key equal 2.... Check whether the two VNodes are equal
    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)
            )
        )
    )
}
// 根据index判定, 如果新增node  则createElm
function addVnodes (parentElm, refElm, vnodes, startIdx, endIdx, insertedVnodeQueue) {
    for (; startIdx <= endIdx; ++startIdx) {
        createElm(vnodes[startIdx], insertedVnodeQueue, parentElm, refElm, false, vnodes, startIdx); }}function removeVnodes (vnodes, startIdx, endIdx) {
    for (; startIdx <= endIdx; ++startIdx) {
        var ch = vnodes[startIdx];
        if (isDef(ch)) {
            if (isDef(ch.tag)) {
                removeAndInvokeRemoveHook(ch);
                invokeDestroyHook(ch);
            } else { // Text noderemoveNode(ch.elm); }}}}function removeNode (el) {
    var parent = nodeOps.parentNode(el);
    // element may have already been removed due to v-html / v-text
    if(isDef(parent)) { nodeOps.removeChild(parent, el); }}Copy the code