在 Vue 3 中,插槽(slots)是一种强大的模式,用于将模板代码从父组件注入到子组件中,使得子组件的内容可以在使用时被自定义。Vue 3 中的插槽用法包括基本插槽、具名插槽和作用域插槽。
基本插槽
基本插槽允许父组件向子组件传递内容,这些内容在子组件的模板中通过一个简单的 标签进行定义和显示。
- 子组件
<template>
<div>
<slot></slot> <!-- 默认插槽 -->
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'ChildComponent'
});
</script>
- 父组件
<template>
<div>
<ChildComponent>
<p>This content will go into the default slot of the ChildComponent.</p>
</ChildComponent>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import ChildComponent from './ChildComponent.vue';
export default defineComponent({
name: 'ParentComponent',
components: {
ChildComponent
}
});
</script>
默认内容
可以在 标签内部指定默认内容,如果父组件没有提供任何插槽内容,将显示默认内容。
- 子组件
<template>
<div>
<slot>
Default content if nothing is provided by the parent.
</slot>
</div>
</template>
具名插槽
具名插槽允许你为不同的插槽内容定义多个插槽,每个插槽都有其唯一的名字。这样父组件可以针对特定的插槽提供内容。
- 子组件
<template>
<div>
<slot name="header"></slot>
<slot name="main"></slot>
<slot name="footer"></slot>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'ChildComponent'
});
</script>
- 父组件
<template>
<ChildComponent>
<template v-slot:header>
<h1>Header Content</h1>
</template>
<template v-slot:main>
<p>Main Content of the Page</p>
</template>
<template v-slot:footer>
<footer>Footer Details</footer>
</template>
</ChildComponent>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import ChildComponent from './ChildComponent.vue';
export default defineComponent({
name: 'ParentComponent',
components: {
ChildComponent
}
});
</script>
作用域插槽
作用域插槽(Scoped Slots)是 Vue 中一种高级的插槽用法,允许子组件将其内部的数据传递回给使用这些插槽的父组件的插槽内容。这种方式不仅可以让父组件插入 HTML 或组件,还能让父组件访问子组件中定义的数据,非常适合创建高度可定制和复用的组件。
- 子组件
<template>
<ul>
<!-- 使用作用域插槽将 todo 对象作为插槽的数据传递给父组件 -->
<li v-for="todo in todos" :key="todo.id">
<slot name="todo" :todo-data="todo">
<!-- 默认内容,如果父组件没有提供插槽模板 -->
{{ todo.text }}
</slot>
</li>
</ul>
</template>
<script lang="ts">
import { defineComponent, PropType } from 'vue';
export default defineComponent({
name: 'TodoList',
props: {
todos: Array as PropType<Array<{ id: number; text: string }>>
}
});
</script>
- 父组件
<template>
<TodoList :todos="todoItems">
<!-- 定义如何使用 todo 数据渲染每个待办事项 -->
<template v-slot:todo="{ todo }">
<strong>{{ todo.text }}</strong> (ID: {{ todo.id }})
</template>
</TodoList>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
import TodoList from './components/TodoList.vue';
export default defineComponent({
name: 'App',
components: {
TodoList
},
setup() {
const todoItems = ref([
{ id: 1, text: 'Finish the report' },
{ id: 2, text: 'Meet with the client' },
{ id: 3, text: 'Prepare presentation' }
]);
return { todoItems };
}
});
</script>
子组件提供了一个todo插槽,每一个todo的数据通过todo-data传递给插槽。父组件接收这个数据并自定义了如何显示每个项目。
提示:v-slot:todo 简写 #todo='todo'