解析 Vue3 patchChildren(n1,n2,container) 方法

207 阅读2分钟

patchChildren(n1,n2,container) 当新、旧在子节点的类型都是元素类型时,调用 patchChildren()进行对新节点进行更新

Vue3 元素子节点的三种规范

元素子节点的三种规范:

-   没有子节点 node.children = null
-   一个字符串子节点 node.children = 'string'
-   数组子节点,里面包含各种类型的子节点

```js
// 元素子节点的 type 都是字符串
// 1
const vnode = [
    {
        type: 'p',
        children: null,
    },
]

// 2
const vnode = [
    {
        type: 'p',
        children: '',
    },
]

// 3
const vnode = [
    {
        type: 'p',
        children: [
            {
                type: 'span',
                children: 'text',
            },
            null,
            'some text',
        ],
    },
]
```

新旧子节点的9种类型

-   新子节点 为 null

    -   旧子节点 为 null **(清空就行,setElementText(el, ''))**
    -   旧子节点 为 字符串 **(清空就行,setElementText(el, ''))**
    -   旧子节点 为 数组 **(逐个卸载就行)**

-   新子节点 为 字符串

    -   旧子节点 为 null **(更新内容,setElementText(el,newValue))**
    -   旧子节点 为 字符串 **(更新内容,setElementText(el,newValue))**
    -   旧子节点 为 数组 **(先卸载所有的旧子节点,然后更新内容,setElementText(el,newValue))**

-   新子节点 为 数组
    -   旧子节点 为 null **(清空容器 setElementText(el, ''),逐个挂载新子节点)**
    -   旧子节点 为 字符串 **(清空容器 setElementText(el, ''),逐个挂载新子节点)**
    -   旧子节点 为 数组 **(核心 diff 算法,比较两组差异)**
    
    

api解析

  
    /**
     * 更新节点
     * 新旧子节点共有9种情况,渲染器.md 426 有详细注释
     * @param {*} n1
     * @param {*} n2
     * @param {*} container
     */
    function patchChildren(n1, n2, container) {
        if (typeof n2.children === 'string') {
            /**
             * 处理 新子节点为字符串的情况
             * 这种情况下 旧子节点 为 null | string 都不需要特殊处理
             * 只需要处理旧子节点为数组的情况,需要卸载旧子节点
             *
             */
            if (Array.isArray(n1.children)) {
                // 旧子节点为数组的情况,需要卸载旧子节点
                n1.children.forEach((node) => unmount(node))
            }

            setElementText(container, n2.childern) // 最后都把内容置为字符串
        } else if (Array.isArray(n2.children)) {
            /**
             * 处理 新子节点 为数组的情况
             */
            if (Array.isArray(n1.children)) {
                /**
                 *
                 * 旧子节点为数组,diff算法
                 * diff 算法
                 */
            } else {
                /**
                 *
                 * 旧子节点为字符串 | 为 null
                 * 清空容器,逐个挂载新子节点
                 */
                setElementText(container, '')
                n2.children.forEach((node) => patch(null, node, container))
            }
        } else {
            /**
             * 新子节点为 null
             *
             */
            if (Array.isArray(n1.children)) {
                // 旧子节点为数组,逐个卸载
                n1.children.forEach((node) => unmount(node))
            } else if (typeof n1.children === 'string') {
                // 字符串的子节点,清空容器
                setElementText(container, '')
            }
            // 为 null 就表示没有子节点,无需处理
        }
    }