代码运行结果
代码示例
<script src="./dist/vue.global.js"></script>
<body>
<div id="app"></div>
<script>
let { createApp, reactive, Fragment, toRefs, h, getCurrentInstance } = Vue
const MyCpn = {
setup(props, { emit, slots }) {
console.log(getCurrentInstance(), 'child')
return () =>
h(Fragment, [
h('div', slots.header()),
h('div', slots.main()),
h('div', slots.footer())
])
}
}
const App = {
render() {
return h(MyCpn, null, {
header: () => {
return h('h1', 'header')
},
main: () => {
return h('h1', 'main')
},
footer: () => {
return h('h1', 'footer')
}
})
}
}
createApp(App).mount('#app')
</script>
第一:挂载子组件的时候,通过 h, createVNode 函数,创建虚拟 Dom 的时候,在 normalizeChildren 函数中 判断第三个参数是对象,则给子组件的示例上 ShapeFlags 标记了 SLOTS_CHILDREN。
type = ShapeFlags.SLOTS_CHILDREN
vnode.shapeFlag |= type
第二:挂载子组件的时候,调用 mountComponent 时,调用 initSlots 函数,来给 instance.slots 上赋值 children 属性。也就是:
{slots: {
header: () => {
return h('h1', 'header')
},
main: () => {
return h('h1', 'main')
},
footer: () => {
return h('h1', 'footer')
}
}}
export const initSlots = (
instance: ComponentInternalInstance,
children: VNodeNormalizedChildren
) => {
if (instance.vnode.shapeFlag & ShapeFlags.SLOTS_CHILDREN) {
const type = (children as RawSlots)._
if (type) {
// users can get the shallow readonly version of the slots object through `this.$slots`,
// we should avoid the proxy object polluting the slots of the internal instance
instance.slots = toRaw(children as InternalSlots)
// make compiler marker non-enumerable
def(children as InternalSlots, '_', type)
} else {
normalizeObjectSlots(
children as RawSlots,
(instance.slots = {}),
instance
)
}
} else {
instance.slots = {}
if (children) {
normalizeVNodeSlots(instance, children)
}
}
def(instance.slots, InternalObjectKey, 1)
}
第三:在子组件上通过 调用 slots.header(), 再调用 h 函数 获得虚拟 Dom,挂载的时候调用 render 函数进行 patch(这里没有 render 函数,就会将 setup 返回的函数当作 render 函数)。
自此挂载完毕。