更新子节点
前面实现的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, '');
}
}