看下面HTML代码:
<div>
<!-- 注释节点 -->
我是文本节点
</div>
上面的代码中包含,元素节点<div>
,注释节点和文本节点。那么如何用vnode描述注释节点和文本节点?
要注意的是注释节点与文本节点不同于普通标签节点,不具有标签名称,所以要人为创造一些唯一的标识,并将其作为type属性值,如下面代码:
// 文本节点
const Text = Symbol()
const newVnode = {
// 描述文本节点
type: Text,
children: '我是文本内容'
}
// 注释节点的type标识
const Comment = Symbol()
const newVnode = [
type: Comment,
children:'我是注释内容'
}
这样就能用vnode来描述文本节点和注释节点了。由于文本节点和注释节点只关心文本内容,所以用vnode.children来存储它们对应的文本内容
有了vnode后,就可以用渲染器来渲染了,如下面代码所示:
function patch(n1,n2,container){
if(n1 && n1.type !== n2.type){
unmount(n1)
n1 = null
}
const {type} = n2
if(typeof type === 'string'){
/.../
}else if(type === Text){
// 如果是文本节点
if(!n1){
// 如果没有旧节点,则进行挂载
// 使用createTextNode创建文本节点
const el = n2.el = document.createTextNode(n2.children)
// 将文本节点插入到容器中
insert(el,container)
}else{
// 如果旧vnode存在,只需要使用新文本节点的文本内容更新旧文本节点即可
const el = n2.el = n1.el
if(n2.children !== n1.children){
el.nodeValue = n2.children
}
}
}
}
注意这里使用的是文本节点的nodeValue属性来完成文本内容的更新
此外上面代码使用到的createTextNode和el.nodeValue都是浏览器平台特有的API,为了保证渲染器核心的跨平台能力,需要将这两个操作DOM的API封装到渲染器的选项中,如下面代码:
const renderer = createRenderer({
/.../
createText(text){
return document.createTextNode(text)
}
setText(el,text){
el.nodeValue = text
}
/.../
})
接着再优化patch代码如下:
function patch(n1,n2,container){
if(n1 && n1.type !== n2.type){
unmount(n1)
n1 = null
}
const {type} = n2
if(typeof type === 'string'){
/.../
}else if(type === Text){
// 如果是文本节点
if(!n1){
const el = n2.el = createText(n2.children)
insert(el,container)
}else{
const el = n2.el = n1.el
if(n2.children !== n1.children){
setText(el,n2.children)
}
}
}
}