Vue3 / Vue2 Slot
读过 Vue3 的官方文档之后,我惊奇的发现,Vue3 的插槽slot的使用方式与Vue 2是基本没有任何变化的,相比Vue3引进了大量的组合式API,这无非让人在Vue3中找到了Vue2的影子。
Slot的基本使用(默认插槽、匿名插槽)
- 匿名插槽顾名思义就是没有名字的插槽,也是在Vue中使用最普遍的插槽方式。 并且在一个组件中只能使用一个这样的匿名插槽。
// 父组件
<template>
<div class="father">
<h3>这里是父组件</h3>
<child>
<div class="tmpl">
<span>菜单1</span>
<span>菜单2</span>
<span>菜单3</span>
<span>菜单4</span>
<span>菜单5</span>
<span>菜单6</span>
</div>
</child>
</div>
</template>
// 子组件
<template>
<div class="child">
<h3>这里是子组件</h3>
<slot></slot>
</div>
</template>
- 默认内容 在外部没有提供任何内容的情况下,可以为插槽指定默认内容。
<template>
<div class="child">
<h3>这里是子组件</h3>
<slot>
<p>我是默认内容</p>
</slot>
</div>
</template>
解释:将默认内容写在标签之间来做默认的显示内容;但是如果在父组件中提供了插槽内容,那么子组件的默认内容将会被 显示提供的内容所取代。
slot具名插槽
- 当slot具有了name属性,就是具名插槽,具名插槽可以在一个组件中出现多次,并且可以使用v-slot指令来可以规定每个插槽的指定位置;
// 子组件BaseLayout
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot> // 没有提供name的<slot>会隐式的将值赋值为default。
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
// (子组件)在父组件中使用时;
<BaseLayout>
<template v-slot:header>
<!-- header 插槽的内容放这里 -->
</template>
</BaseLayout>
注意:v-slot有对应的简写#,因此因此 <template v-slot:header> 可以简写为 <template #header>
// 完整代码:
<BaseLayout>
<template #header>
<h1>Here might be a page title</h1>
</template>
<template #default>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</template>
<template #footer>
<p>Here's some contact info</p>
</template>
</BaseLayout>
动态插槽名
<base-layout>
<template v-slot:[dynamicSlotName]>
...
</template>
<!-- 缩写为 -->
<template #[dynamicSlotName]>
...
</template>
</base-layout>
动态插槽名适用于不同的业务场景渲染不一样的内容,这在业务开发中是非常常用的。
作用域插槽
-
a. 插槽的内容无法访问到子组件的状态;在某些情况下插槽的内容可能想要同时使用父组件域内和子组件域内的数据。
// 让子组件在渲染时将一部分数据提供给插槽。
<!-- <MyComponent> 的模板 -->
<div>
<slot :text="greetingMessage" :count="1"></slot>
</div>
// 默认插槽如何接受props
<MyComponent v-slot="slotProps">
{{ slotProps.text }} {{ slotProps.count }}
</MyComponent>
// 或者使用解构
<MyComponent v-slot="{ text, count }">
{{ text }} {{ count }}
</MyComponent>
b. 具名作用域插槽
<MyComponent>
<template #header="headerProps">
{{ headerProps }}
</template>
<template #default="defaultProps">
{{ defaultProps }}
</template>
<template #footer="footerProps">
{{ footerProps }}
</template>
</MyComponent>
// 向具名插槽中传递props
<slot name="header" message="hello"></slot>
注意插槽上的 name 是一个 Vue 特别保留的 attribute,不会作为 props 传递给插槽。因此最终 headerProps 的结果是 { message: 'hello' }。
注意:如果你混用了匿名插槽与具名插槽,则需要默认插槽使用显式的template标签,如果直接给匿名组件直接添加v-slot指令将导致编译错误,这是因为匿名插槽的作用域导致的。
<!-- 该模板无法编译 -->
<template>
<MyComponent v-slot="{ message }">
<p>{{ message }}</p>
<template #footer>
<!-- message 属于默认插槽,此处不可用 -->
<p>{{ message }}</p>
</template>
</MyComponent>
</template>
更改后:
<template>
<MyComponent>
<!-- 使用显式的默认插槽 -->
<template #default="{ message }">
<p>{{ message }}</p>
</template>
<template #footer>
<p>Here's some contact info</p>
</template>
</MyComponent>
</template>