Vue3 diff算法的暴力写法

170 阅读1分钟

以下代码仅为diff算法的暴力解法,源码更为复杂且高效。mountElement 方法在上一篇文章中

/*
    n1: 老节点
    n2: 新节点
*/
export function diff(n1, n2) {
    // tag
    // props
    // children

    // tag不同直接替换
    if (n1.tag !== n2.tag) {
        n1.el.replaceWith(document.createElement(n2.tag));
    }
    else if (n1.props !== n2.props) {
        // props不同分为三种情况
        // 1.value值发生了变化
        // 2.新节点的props增加
        // 3.新节点的props减少

        // 把el进行交换,保证下一次的diff正确
        n2.el = n1.el;

        const { props: oldProps } = n1;
        const { props: newProps } = n2;

        if (newProps) {
            Object.entries(newProps).forEach(([key, value]) => {
                const oldValue = oldProps[key];
                if (value !== oldValue) {
                    n1.el.setAttribute(key, value);
                }
            });
        }
        
        if (oldProps) {
            Object.keys(oldProps).forEach((key) => {
                if (!newProps[key]) {
                    n1.el.removeAttribute(key);
                }
            });
        }

        // children 暴力解法
        const {children: newChildren} = n2;
        const {children: oldChildren} = n1;

        if (typeof newChildren === 'string') {
            if (typeof oldChildren === 'string') {
                if (newChildren !== oldChildren) {
                        n2.el.textContent = newChildren;
                }
            }
            else if (Array.isArray(oldChildren)) {
                n2.el.textContent = newChildren;
            }
        }
        else if (Array.isArray(newChildren)) {
            if (typeof oldChildren === 'string') {
                n2.el.innerText = ``;
                mountElement(n2, n2.el);
            }
            else if (Array.isArray(oldChildren)) {
                // 对比新老children的长度
                // 相同长度部分进行递归diff
                // newChildren长的部分做创建处理
                // oldChildren长的部分做删除处理
                const length = Max.min(newChildren.length, oldChildren.length);

                for (let i = 0; i < length; i++) {
                    const newvNode = newChildren[i];
                    const oldvNode = oldChildren[i];
                    diff(oldvNode, newvNode);
                }

                if (newChildren.length > length) {
                    for (let i = length; i < newChildren.length;i++) {
                        const newvNode = newChildren[i];
                        mountElement(newvNode, n2.el);
                    }
                }

                if (oldChildren.length > length) {
                    for (let i = length; i < oldChildren.length; i++) {
                        const oldvNode = newChildren[i];
                        // 其实应该是createElement创建出节点,然后删除掉。这里就简单处理了
                        oldvNode.el.parent.removeChild(oldvNode);
                    }
                }
            }
        }
    }
}