8.10 文本节点和注释节点
文本节点与注释节点没有标签名称,无法用字符串作为
vnode.type,于是人为创造唯一的 Symbol 标识。
一、用 Symbol 作为类型标识
vnode.type 的值是字符串时,代表它描述的是普通标签,并且该值就是标签名称;文本节点与注释节点不同,需要额外的唯一标识:
// 文本节点的 type 标识
const Text = Symbol()
// 注释节点的 type 标识
const Comment = Symbol()
对应的 vnode 描述:
// 描述文本节点
{ type: Text, children: '我是文本内容' }
// 描述注释节点
{ type: Comment, children: '我是注释内容' }
二、patch 中处理文本节点
在 patch 中增加 type === Text 的判断;同时还要判断旧的虚拟节点(n1)是否存在 —— 不存在就直接挂载,存在则更新文本内容:
} else if (type === Text) {
// 新 vnode 的类型是 Text → 描述的是文本节点
if (!n1) {
// 没有旧节点 → 挂载:创建文本节点并插入容器
const el = n2.el = createText(n2.children)
insert(el, container)
} else {
// 旧 vnode 存在 → 用新文本内容更新旧文本节点
const el = n2.el = n1.el
if (n2.children !== n1.children) {
setText(el, n2.children)
}
}
}
注释节点的处理方式与文本节点类似,区别只在于创建时要使用 document.createComment:
} else if (type === Comment) {
if (!n1) {
const el = n2.el = createComment(n2.children)
insert(el, container)
} else {
const el = n2.el = n1.el
if (n2.children !== n1.children) {
setText(el, n2.children)
}
}
}
三、跨平台:把 DOM API 收进渲染器选项
为了保持渲染器核心的跨平台能力,创建与更新文本节点所涉及的两个 DOM API(document.createTextNode 与 el.nodeValue)也要封装到渲染器选项中:
const renderer = createRenderer({
// ...
createText(text) {
return document.createTextNode(text)
},
setText(el, text) {
el.nodeValue = text
},
createComment(text) {
return document.createComment(text)
}
})
| 节点类型 | type | 挂载 | 更新内容 |
|---|---|---|---|
| 文本节点 | Text | createText(children) | setText(el, children) |
| 注释节点 | Comment | createComment(children) | setText(el, children) |
文本节点与注释节点都用
el.nodeValue读写内容,所以更新逻辑可以共用setText。
相关
- 8.9 更新子节点:子节点中出现文本 / 注释节点时的更新路径
- 8.11 Fragment:另一种没有标签名称的 vnode 类型