8.6 区分vnode类型
打补丁的前提是新旧 vnode 所描述的内容相同;类型不同时打补丁没有意义,应该直接卸载旧的、挂载新的。
一、类型不同 → 整体替换
初次渲染 p 元素、后续渲染 input 元素时,两者之间不存在打补丁的意义:不同的元素各有特有的属性(input 有 type,p 没有)。所以在执行更新前先比较 type:
function patch(n1, n2, container) {
// 如果 n1 存在,则对比 n1 和 n2 的类型
if (n1 && n1.type !== n2.type) {
// 类型不同 → 直接卸载旧 vnode
unmount(n1)
n1 = null
}
// 代码运行到这里,证明 n1 和 n2 所描述的内容相同
// ...
}
必须把
n1重置为null:卸载之后旧节点已经不存在,置空后代码才会走「挂载」分支;否则会拿一个已被移除的节点去打补丁。
二、按 type 的值分派处理方式
即使新旧类型相同,也还要确认 vnode 描述的是什么,再选择不同的挂载 / 打补丁方式:
const { type } = n2
if (typeof type === 'string') {
// 普通标签元素
if (!n1) {
mountElement(n2, container)
} else {
patchElement(n1, n2)
}
} else if (type === Text) {
// 文本节点
} else if (type === Comment) {
// 注释节点
} else if (type === Fragment) {
// 片段
} else if (typeof type === 'object') {
// 组件
}
vnode.type 的形态 | vnode 描述的内容 | 处理方式 |
|---|---|---|
| 字符串 | 普通标签元素 | mountElement / patchElement |
Symbol(Text、Comment、Fragment) | 文本节点 / 注释节点 / 片段 | 各自的挂载与更新逻辑 |
| 对象 | 组件 | 组件的挂载与更新逻辑 |
Text、Comment、Fragment都没有标签名称,只能人为创造唯一标识作为type(见 8.10、8.11)。