假设我们有如下模板:
<MyComponent>
<div></div>
</MyComponent>
@前端进阶之旅: 代码已经复制到剪贴板
由这段模板可知,我们为 MyComponent 组件提供了一个空的 div 标签作为默认插槽内容,从DOM结构上看 <MyComponent> 标签有一个 div 标签作为子节点,通常我们可以将其编译为如下 VNode:
const compVNode = {
flags: VNodeFlags.COMPONENT_STATEFUL_NORMAL,
tag: MyComponent,
children: {
flags: VNodeFlags.ELEMENT,
tag: 'div'
}
}
@前端进阶之旅: 代码已经复制到剪贴板
这其实没什么问题,但是我们更倾向于新建一个 slots 属性来存储这些子节点,这在语义上更加贴切,所以我们希望将模板编译为如下 VNode:
const compVNode = {
flags: VNodeFlags.COMPONENT,
tag: MyComponent,
children: null,
slots: {
// 默认插槽
default: {
flags: VNodeFlags.ELEMENT,
tag: 'div'
}
}
}
@前端进阶之旅: 代码已经复制到剪贴板
可以看到,如上 VNode 的 children 属性值为 null。当我们使用 mountComponent 函数挂载如上 VNode 时,我们可以在组件实例化之后并且在组件的渲染函数执行之前将 compVNode.slots 添加到组件实例对象上,这样当组件的渲染函数执行的时候,就可以访问插槽数据:
function mountComponent(vnode, container) {
// 创建组件实例
const instance = new vnode.tag()
// 设置 slots
instance.$slots = vnode.slots
// 渲染
instance.$vnode = instance.render()
// 挂载
mountElement(instance.$vnode, container)
vnode.