持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第14天,点击查看活动详情
上一篇文章我们实现了Fragment类型的vnode,这章来实现另一种类型的vnode -- Text类型
Text类型的vnode会被直接渲染成文本,不会有任何标签包裹,像之前如果我们需要渲染文本的话,是需要一个父标签包裹的,比如p标签、span标签等,而Text类型的vnode就能够打破这个限制
1. 分析要做的事情
有了前面Fragment的经验,现在实现Text就简单很多了,可以完全依葫芦画瓢,大概就是以下几步:
- 创建一个新的
vnode的type--Text,用Symbol实现 patch的时候添加对type为Text的特殊处理- 调用原生
js的createTextNode去创建文本结点 - 将文本结点作为
vnode.el添加到container中
2. 使用场景
事实上就和处理Element类型是类似的,只是创建的不是普通结点,而是TextNode
无论是slots中要渲染的内容还是直接渲染的内容,都需要是vnode才行,因此我们不能像下面这样尝试直接渲染纯文本
import { h } from '../../lib/plasticine-mini-vue.esm.js';
import { Foo } from './Foo.js';
export const App = {
name: 'App',
setup() {
return {};
},
render() {
const foo = h(
Foo,
{},
{
// 不能直接渲染纯文本 因为 string 不是 vnode
header: ({ age }) => [h('p', {}, 'header' + age), 'Text Node Content'],
footer: () => h('p', {}, 'footer'),
}
);
// const foo = h(Foo, {}, h('p', {}, 'default slot'));
return h('div', {}, [foo]);
},
};
3. createTextVNode代替h生成文本vnode
我们可以实现一个createTextVNode函数,创建一个type为Text的vnode
import { h } from '../../lib/plasticine-mini-vue.esm.js';
import { Foo } from './Foo.js';
export const App = {
name: 'App',
setup() {
return {};
},
render() {
const foo = h(
Foo,
{},
{
- header: ({ age }) => [h('p', {}, 'header' + age), 'Text Node Content'],
+ header: ({ age }) => [
+ h('p', {}, 'header' + age),
+ createTextVNode('Text Node Content'),
+ ],
footer: () => h('p', {}, 'footer'),
}
);
// const foo = h(Foo, {}, h('p', {}, 'default slot'));
return h('div', {}, [foo]);
},
};
在vnode.ts中实现createTextVNode以及定义一个Text类型
export const Text = Symbol('Text');
export function createTextVNode(text: string) {
return createVNode(Text, {}, text);
}
4. patch中增加对Text类型的处理
再在patch中实现对Text类型vnode的处理
export function patch(vnode, container) {
const { type, shapeFlag } = vnode;
switch (type) {
case Fragment:
processFragment(vnode, container);
break;
case Text:
processText(vnode, container);
break;
default:
if (shapeFlag & ShapeFlags.ELEMENT) {
// 真实 DOM
processElement(vnode, container);
} else if (shapeFlag & ShapeFlags.STATEFUL_COMPONENT) {
// 处理 component 类型
processComponent(vnode, container);
}
break;
}
}
function processText(vnode: any, container: any) {
const { children } = vnode;
const textNode = (vnode.el = document.createTextNode(children));
container.append(textNode);
}
和处理Element类型的vnode是很类似的,只不过Element类型的vnode需要先创建一个父容器,然后根据children是文本还是数组去执行不同的渲染逻辑
而Text则简单很多,不需要任何父容器,直接创建TextNode即可
5. runtime-core出口导出createTextVNode
最后别忘了在runtime-core的出口index.ts中将createTextVNode函数导出,从而能够被rollup打包到构建结果中
export { createApp } from './createApp';
export { h } from './h';
export { renderSlots } from './helpers/renderSlots';
export { createTextVNode } from './vnode';