解析 Vue3 diff 算法中 key的作用

206 阅读1分钟

key 的作用,新旧子节点对比的过程中,如果新旧子节点的 type & key 相同,就认为是同一个节点,确定新子节点在旧子节点中的位置,方便进行 dom 移动,并且会复用该旧节点,对该旧节点进行更新

利用 node.type 无法确定在旧节点中新节点的位置

/**
 * 当节点的type都相同时,无法通过type来确定同一元素
 */
const oldNode = [
    {
        type: 'p',
        children: 1,
    },
    {
        type: 'p',
        children: 2,
    },
]
const newNode = [
    {
        type: 'p',
        children: 3,
    },
]
/**
 * 当节点的type都相同时,无法通过type来确定同一元素,oldNode 里面两个元素的类型都是 p,无法确定唯一性
 */

利用 node.type & node.key 来确定在旧节点中新节点的位置

// 添加 key属性,利用 type & key 属性来确定同一元素
const oldNode = [
    {
        type: 'p',
        children: 1,
        key: '1',
    },
    {
        type: 'p',
        children: 2,
        key: '2',
    },
]
const newNode = [
    {
        type: 'p',
        children: 3,
        key: '1',
    },
]
// oldNode 里面 类型是p并且key为 '1' 只有一个元素,新节点里面也有相同的元素,能够确定唯一性

diff 算法 根据 type & key 确定相同的子节点

const oldNode = {
    type: 'div',
    children: [
        {
            type: 'p',
            children: 1,
            key: '1',
        },
        {
            type: 'p',
            children: 2,
            key: '2',
        },
    ],
}
const newNode = {
    type: 'div',
    children: [
        {
            type: 'p',
            children: 3,
            key: '1',
        },
    ],
}
function patchChildren(n1, n2, container) {
    if (typeof n2.children === 'string') {
        // ...
    } else if (Array.isArray(n2.children)) {
        if (Array.isArray(n1.children)) {
            const oldChildren = n1.children
            const newChildren = n2.children

            for (let i = 0; i < newChildren.length; i++) {
                /**
                 * 两层循环,找新节点在旧节点中的位置
                 */
                const newNode = newChildren[i]
                for (let j = 0; j < oldChildren.length; j++) {
                    const oldNode = oldChildren[j]
                    if (
                        newNode.type === oldNode.type &&
                        newNode.key === oldNode.key
                    ) {
                        /**
                         * key & type 都相同,确定是同一个节点,复用更新
                         * 此时,oldNode 和 newNode 是相同的元素,patch 会对这两个元素进行更新,而不是先卸载 在 挂载
                         */
                        patch(oldNode, newNode, container)
                        break
                    }
                }
            }
        }
    }
}