前言
在 Vue.js 中,插槽(slot)是一个非常重要的概念,它让我们可以在组件之间实现内容分发。虽然 Vue.js 的官方文档对插槽进行了详细的介绍,但初次接触时可能会觉得有点复杂,什么默认插槽、具名插槽、作用域插槽、动态插槽等。
一个例子
假如我们现在有一个组件,组件中有多个插槽:
<!-- Child.vue -->
<template>
<div>
<slot />
<slot name="slot1" />
<slot name="slot2" msg="Hello, world" />
</div>
</template>
那么,我们在父组件中导入子组件并使用插槽:
<!-- Parent.vue -->
<VueSlot>
<p>Default Slot</p>
<template #slot1>
<p>Slot 1</p>
</template>
<template #slot2="{ msg }">
<p>Slot 2: {{ msg }}</p>
</template>
</VueSlot>
很多人可能会以为 slot
就是简单的占位符,用来在父组件和子组件之间传递内容。但实际上,在父组件中使用插槽传递内容给子组件的过程,是将一个对象传递给子组件。
如果我们用 JSX 语法来重写上面的例子,它的本质会变得更加明显:
{
default: () => <p>Default Slot</p>,
slot1: () => <p>Slot 1</p>,
slot2: (props) => <p>Slot 2: {props.msg}</p>
}
上面的代码表示,父组件实际上将一个对象传递给了子组件,键是插槽的名称,值是返回虚拟节点的函数。
Vue.js 的虚拟节点创建
为了更深入地理解插槽,我们可以借助 Vue.js 的 createElementVNode
函数来手动创建虚拟节点。这可以帮助我们看到插槽内容如何在底层被处理。javascript
export default {
setup() {
return () => {
return createElementVNode("div", null, []);
};
},
};
当我们使用 createElementVNode
创建虚拟节点时,我们实际上是在手动构建 Vue.js 内部的 VNode 结构,这与插槽传递的内容形式是类似的。
·
在 JavaScript 中操作插槽
既然插槽是以对象的形式传递的,我们自然可以在子组件中访问这个对象。下面我们来看一下如何在 setup
函数中访问插槽对象:
export default {
setup(props, { slots }) {
console.log(slots);
return () => {
return createElementVNode("div", null, []);
};
},
};
在控制台打印出来后我们会发现,与上文分析的结果是一样的。通过 slots
对象,我们可以访问传递过来的插槽内容,并且这些插槽内容以函数的形式存在。调用这些函数时,我们会得到相应的虚拟节点。
多个默认插槽
那为什么打印出来的是一个数组,因为默认的插槽我们可以传递多个虚拟节点。
<VueSlot>
<p>Default Slot</p>
<p>Default Slot</p>
<p>Default Slot</p>
<p>Default Slot</p>
<p>Default Slot</p>
<p>Default Slot</p>
<template #slot1>
<p>Slot 1</p>
</template>
<template #slot2="{ msg }">
<p>Slot 2: {{ msg }}</p>
</template>
</VueSlot>
接下来我们就可以决定这些虚拟节点可以渲染到什么位置。
setup(props, { slots }) {
const defaultVNodes = slots.default()
return () => {
return createElementVNode('div', null, [
...defaultVNodes
])
}
}
这就是插槽的本质,传递一个对象给子组件,子组件接收这个虚拟节点对象,然后自行决定渲染在哪里。