vue渲染器(二)

70 阅读3分钟

更新子节点

前面实现的patch函数中有一个patchElement函数还没有实现,就是当新旧节点类型相同时,我们需要调用更新普通节点的方法


                //如果新节点与旧节点类型相同
                function patchElement(n1, n2) {
                    const el = (n2.el = n1.el);
                    const oldProps = n1.props;
                    const newProps = n2.props;
                    //第一步:更新props
                    for (const key in newProps) {
                        if (newProps[key] !== oldProps[key])
                            patchProps(el, key, oldProps[key], newProps[key]);
                    }
                    for (const key in oldProps) {
                        if (!(key in oldProps)) patchProps(el, key, oldProps[key], null);
                    }

                    //第二步更新children
                    patchChildren(n1, n2, el);
                }

第一步主要是对属性继续更新,如果key存在,则进行更新,不存在则卸载

第二步则是更新子节点了调用patchChildren函数

                //更新子节点
                function patchChildren(n1, n2, container) {
                    //判断子节点类型是否为文本节点
                    if (typeof n2.children === 'string') {
                        //当子节点类型为一组子节点就需要遍历卸载,其他情况不用
                        if (Array.isArray(n1.children)) n1.children.forEach((c) => unmount(c));
                        //将新的文本节点设置给容器元素
                        setElementText(container, n2.children);
                    }
                }

除了上面的代码判断节点类型是不是文本节点,还需要多一个判断分支判断是否是一组子节点

                //更新子节点
                function patchChildren(n1, n2, container) {
                    //判断子节点类型是否为文本节点
                    if (typeof n2.children === 'string') {
                        //当子节点类型为一组子节点就需要遍历卸载,其他情况不用
                        if (Array.isArray(n1.children)) n1.children.forEach((c) => unmount(c));
                        //将新的文本节点设置给容器元素
                        setElementText(container, n2.children);
                    } else if (Array.isArray(n2.children)) {
                        //说明新子节点是一组子节点
                        //判断旧子节点是否也是一组子节点
                        if (Array.isArray(n1.children)) {
                            //这里就涉及vue最核心的diff算法了
                        } else {
                            //到这里,旧子节点要么为文本,要么没有
                            setElementText(container, '');
                            n2.children.forEach((c) => patch(null, c, container));
                        }
                    }
                }

如果新旧节点都是数组,也就是多个元素,就要涉及diff算法了,如果旧子节点不是数组,新子节点就循环挂载子节点就行了

                //更新子节点
                function patchChildren(n1, n2, container) {
                    //判断子节点类型是否为文本节点
                    if (typeof n2.children === 'string') {
                        //当子节点类型为一组子节点就需要遍历卸载,其他情况不用
                        if (Array.isArray(n1.children)) n1.children.forEach((c) => unmount(c));
                        //将新的文本节点设置给容器元素
                        setElementText(container, n2.children);
                    } else if (Array.isArray(n2.children)) {
                        //说明新子节点是一组子节点
                        //判断旧子节点是否也是一组子节点
                        if (Array.isArray(n1.children)) {
                            //这里就涉及vue最核心的diff算法了
                            n1.children.forEach((c) => unmount(c));
                            n2.children.forEach((c) => patch(null, c, container));
                        } else {
                            //到这里,旧子节点要么为文本,要么没有
                            setElementText(container, '');
                            n2.children.forEach((c) => patch(null, c, container));
                        }
                    }
                }

为了保证功能可用,我们先把就子节点卸载再重新挂载子节点来代替diff算法执行

接下来就剩下新节点为空的情况,如果旧子节点为数组,就遍历卸载,如果为文本,赋值为空即可

                //更新子节点
                function patchChildren(n1, n2, container) {
                    //判断子节点类型是否为文本节点
                    if (typeof n2.children === 'string') {
                        //当子节点类型为一组子节点就需要遍历卸载,其他情况不用
                        if (Array.isArray(n1.children)) n1.children.forEach((c) => unmount(c));
                        //将新的文本节点设置给容器元素
                        setElementText(container, n2.children);
                    } else if (Array.isArray(n2.children)) {
                        //说明新子节点是一组子节点
                        //判断旧子节点是否也是一组子节点
                        if (Array.isArray(n1.children)) {
                            //这里就涉及vue最核心的diff算法了
                            n1.children.forEach((c) => unmount(c));
                            n2.children.forEach((c) => patch(null, c, container));
                        } else {
                            //到这里,旧子节点要么为文本,要么没有
                            setElementText(container, '');
                            n2.children.forEach((c) => patch(null, c, container));
                        }
                    } else {
                        if (Array.isArray(n1.children)) n1.children.forEach((c) => unmount(c));
                        else if (typeof n1.children === 'string') setElementText(container, '');
                    }
                }