更新子节点
因为VNode是一个树结构,他有很多子 Children,那么就需要依次向下遍历,
这个遍历的方法就是 patchChildren
。
这里比较重要
const patchChildren = (
n1,
n2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
optimized = false
) => {
const c1 = n1 && n1.children
const prevShapeFlag = n1 ? n1.shapeFlag : 0 // 旧节点
const c2 = n2.children
const { patchFlag, shapeFlag } = n2 // 新节点
// fast path
if (patchFlag > 0) {
if (patchFlag & 128 /* KEYED_FRAGMENT */) {
patchKeyedChildren(
c1,
c2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
optimized
)
return
} else if (patchFlag & 256 /* UNKEYED_FRAGMENT */) {
patchUnkeyedChildren(
c1,
c2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
optimized
)
return
}
}
// children有3种可能:文本、数组或没有children。
if (shapeFlag & 8 /* TEXT_CHILDREN */) {
// text children fast path
if (prevShapeFlag & 16 /* ARRAY_CHILDREN */) {
// 新节点是文本,旧节点是数组,直接删除。
unmountChildren(c1, parentComponent, parentSuspense)
}
// 新节点是文本,和旧节点不相同,直接替换。
if (c2 !== c1) {
hostSetElementText(container, c2)
}
} else {
if (prevShapeFlag & 16 /* ARRAY_CHILDREN */) {
if (shapeFlag & 16 /* ARRAY_CHILDREN */) {
// 新节点和旧节点都是数组,diff比较
patchKeyedChildren(
c1,
c2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
optimized
)
} else {
// 新节点不是数组,旧节点是数组,直接删除
unmountChildren(c1, parentComponent, parentSuspense, true)
}
} else {
if (prevShapeFlag & 8 /* TEXT_CHILDREN */) {
// 替换空文本
hostSetElementText(container, '')
}
// mount new if array 挂载一个新的数组子节点
if (shapeFlag & 16 /* ARRAY_CHILDREN */) {
mountChildren(
c2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
optimized
)
}
}
}
}
代码中的patchKeyedChildren
是根据存在的 key 进行 Diff。
代码中的patchUnKeyedChildren
是根据不存在的 key 进行 Diff。
总结:
新节点是文本
- 1 - 旧节点是数组,删除所有字节点
- 2 - 旧节点不是是文本,替换文本
- 3 - 旧节点是空,添加文本
新节点是数组
- 1 - 旧节点是数组,diff比较子节点
- 2 - 旧节点不是数组,删除所有子节点
- 3 - 旧节点是空,挂载新的子节点
新节点是空