vue源码学习之diff算法

204 阅读2分钟

最近在看vue源码,大佬写的代码实在是太牛逼了,看得一头雾水,还好有很多大佬写的源码分析的文章,对比起来看还勉强能看懂。废话不多说了,哈哈。看到diff算法,就想着能不能用两个数组来模拟updateChildren diff的过程,好弄清楚那一大段代码都做了啥,写得比较简单,随便看看就好了,写在这里,权当是做个记录😛

function insertBefore(arr, refItem, newItem) {
        var idx = arr.indexOf(refItem);
        arr.splice(idx, 0, newItem);
    }

function moveItem(arr, refItem, itemToMove) {
    var idxOfMove = arr.indexOf(itemToMove);
    arr.splice(idxOfMove, 1);
    var idxRef = arr.indexOf(refItem);
    arr.splice(idxRef, 0, itemToMove);
}

function addItems(arr, ownerArr, startIdx, endIdx) {
    for (; startIdx <= endIdx; startIdx++) {
        arr.splice(-1, 0, ownerArr[startIdx]);
    }
}

function removeItems(arr, ownerArr, startIdx, endIdx) {
    for (; startIdx <= endIdx; startIdx++) {
        var idx = arr.indexOf(ownerArr[startIdx]);
        if (idx !== -1) {
            arr.splice(idx, 1);
        }
    }
}

function patchArray(oldArr, newArr) {
    // 模拟diff算法,result可看做oldArr映射的DOM
    var result = oldArr.slice(),
        oldStartIdx = 0,
        newStartIdx = 0,
        oldEndIdx = oldArr.length - 1,
        oldStartItem = oldArr[0],
        oldEndItem = oldArr[oldEndIdx],
        newEndIdx = newArr.length - 1,
        newStartItem = newArr[0],
        newEndItem = newArr[newEndIdx],
        idxInOld, itemToMove;

    while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
        if (!oldStartItem) {
            oldStartItem = oldArr[++oldStartIdx];
        }
        else if (!oldEndItem) {
            oldEndItem = oldArr[--oldEndIdx];
        }
        else if (oldStartItem === newStartItem) {
            console.log('Start item is equal: %s %s', oldStartItem, newStartItem);
            oldStartItem = oldArr[++oldStartIdx];
            newStartItem = newArr[++newStartIdx];
        }
        else if (oldEndItem === newEndItem) {
            console.log('End item is equal: %s %s', oldEndItem, newEndItem);
            oldEndItem = oldArr[--oldEndIdx];
            newEndItem = newArr[--newEndIdx];
        }
        else if (oldStartItem === newEndItem) {
            console.log('Old start item need insert before previous of oldEndItem');
            moveItem(result, oldEndItem, oldStartItem);
            oldStartItem = oldArr[++oldStartIdx];
            newEndItem = newArr[--newEndIdx];
        }
        else if (oldEndItem === newStartItem) {
            console.log('Old end item need insert before previous of oldStartItem');
            moveItem(result, oldStartItem, oldEndItem);
            oldEndItem = oldArr[--oldEndIdx];
            newStartItem = newArr[++newStartIdx];
        }
        else {
            idxInOld = oldArr.indexOf(newStartItem);
            // new item
            if (idxInOld === -1) {
                console.log('create new item insert before previous of oldStartItem');
                insertBefore(result, oldStartItem, newStartItem);
            }
            // item 在old中,需要移动
            else {
                itemToMove = oldArr[idxInOld];
                console.log('move itemToMove: %s to previous of oldStartItem', itemToMove);
                oldArr[idxInOld] = undefined;
                moveItem(result, oldStartItem, itemToMove);
            }
            newStartItem = newArr[++newStartIdx];
        }
    }
    if (oldStartIdx > oldEndIdx) { // oldArr已经遍历完了,newArr剩下的元素做新增处理
        console.log('将newArr中 newStartIdx: %s 至 newEndIdx: %s间的元素插入', newStartIdx, newEndIdx);
        addItems(result, newArr, newStartIdx, newEndIdx);
    }
    else if (newStartIdx > newEndIdx){ // newArr已经遍历完了,oldArr剩下的元素做删除处理
        console.log('oldArr中 %s 至 %s 的item需要移除', oldStartIdx, oldEndIdx);
        removeItems(result, oldArr, oldStartIdx, oldEndIdx);
    }
    return result;
}
var a1 = ['a', 'b', 'c', 'd', 'e'];
var a2 = ['a', 'c', 'e', 'f', 'g'];
console.log(patchArray(a1, a2));

copy上面这段代码,加个debugger,模拟几种情况,就能知道diff过程大概是怎么回事了