Vue3-插槽slot

插槽是 Vue 组件中一个非常核心的概念,它允许你以一种灵活的方式将内容"插入"到子组件的指定位置,极大地提高了组件的复用性和灵TA性。插槽允许组件只负责渲染一个"框架"(比如边框、阴影),而把"内容"的决定权交给使用它的父组件。

1.默认插槽

最简单的插槽,子组件中只有一个未命名的 <slot> 出口。

子组件

ts 复制代码
<template>
  <div class="card">
    <h3>卡片标题</h3>
    <slot></slot> </div>
</template>

<style scoped>
.card {
  border: 1px solid #ccc;
  border-radius: 8px;
  padding: 16px;
  max-width: 300px;
}
</style>

父组件

ts 复制代码
<template>
  <BaseCard>
    <p>这是一父组件</p>
    <img src="./assets/logo.png" alt="Vue Logo" style="width: 100px;">
  </BaseCard>
</template>

<script setup lang="ts">
import BaseCard from './components/BaseCard.vue';
</script>

2.具名插槽

当子组件需要多个"坑位"时(例如,一个用于头部,一个用于底部),就需要使用具名插槽。

子组件:使用 name 属性来区分不同的插槽。

ts 复制代码
<template>
  <div class="modal">
    <header class="modal-header">
      <slot name="header"></slot> </header>

    <main class="modal-body">
      <slot></slot> </main>

    <footer class="modal-footer">
      <slot name="footer"></slot> </footer>
  </div>
</template>

<style scoped>
.modal { background: #fff; border: 1px solid #ddd; }
.modal-header, .modal-footer { padding: 10px; background: #f4f4f4; }
.modal-body { padding: 20px; }
</style>

父组件:使用 <template> 标签和 v-slot 指令(或其简写 #)来指定要填充的插槽。

ts 复制代码
<template>
  <ModalLayout>
    <template v-slot = "header">
      <h2>这是一个模态框标题</h2>
    </template>

    <p>这是模态框的主要内容...</p>

    <template #footer>
      <button>取消</button>
      <button>确认</button>
    </template>
  </ModalLayout>
</template>

<script setup lang="ts">
import ModalLayout from './components/ModalLayout.vue';
</script>

3.作用域插槽

这是插槽最强大的功能。它允许子组件向父组件的插槽内容传递数据。这在处理列表渲染时非常有用,子组件负责数据迭代,而父组件负责定义每一项的渲染样式。

子组件:子组件通过在 <slot> 标签上绑定属性,来将数据"暴露"给父组件。

ts 复制代码
<template>
  <div class="user-list">
    <p>用户列表:</p>
    <ul>
      <li v-for="user in users" :key="user.id">
        <slot :user="user" :isAdmin="user.name === 'Alice'"></slot>
      </li>
    </ul>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';

interface User {
  id: number;
  name: string;
  age: number;
}

const users = ref<User[]>([
  { id: 1, name: 'Alice', age: 30 },
  { id: 2, name: 'Bob', age: 25 },
]);
</script>

父组件:父组件通过 v-slot (或 #) 接收子组件传递的数据,并且可以立即为这些数据添加 TypeScript 类型

ts 复制代码
<template>
  <UserList>
    <template #default="{ user, isAdmin }: { user: User, isAdmin: boolean }">
      <span> 
        {{ user.name }} ({{ user.age }}岁)
      </span>
      <span v-if="isAdmin" style="color: red; margin-left: 10px;">[管理员]</span>
    </template>
  </UserList>
</template>

<script setup lang="ts">
import UserList from './components/UserList.vue';

// 我们可以在父组件中也定义这个类型,以便复用
interface User {
  id: number;
  name: string;
  age: number;
}
</script>

4.自定义插槽(Vue 3.3+)

在 Vue 3.3 及更高版本中, <script setup> 提供了 defineSlots 宏,这是在子组件中为插槽提供类型 的官方方式。这极大地改善了开发体验,父组件不再需要手动声明类型,因为 TS 可以自动从子组件推断它们。

子组件:使用 defineSlots 来声明插槽及其期望的 props 类型。

ts 复制代码
<template>
  <div class="list">
    <ul>
      <li v-for="(item, index) in items" :key="item.id">
        <slot :item="item" :index="index"></slot>
      </li>
    </ul>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';

// 1. 定义数据和类型
interface Item {
  id: string;
  text: string;
}
const items = ref<Item[]>([
  { id: 'a', text: '第一项' },
  { id: 'b', text: '第二项' },
]);

// 2. (重点) 使用 defineSlots 
// 这是一个宏,无需导入
// 它定义了 'default' 插槽会接收一个对象,该对象包含一个 'item' 属性 (类型为 Item)
// 和一个 'index' 属性 (类型为 number)
defineSlots<{
  default(props: { item: Item; index: number }): any;
  // 如果有具名插槽,也可以在这里定义,比如:
  // header(props: { title: string }): any;
}>();
</script>

父组件:父组件的 slotProps (或解构的变量) 会被自动推断出正确的类型

ts 复制代码
<template>
  <TypedList>
    <template #default="{ item, index }">
      <strong>{{ index + 1 }}.</strong> {{ item.text.toUpperCase() }}
      </template>
  </TypedList>
</template>

<script setup lang="ts">
import TypedList from './components/TypedList.vue';
</script>

5.总结

  • 布局组件 (Layout.vue):

    • 场景: 定义网站的通用布局,如侧边栏、顶部导航和内容区域。
    • 用法 : 使用 header, sidebar, main 等具名插槽,让不同页面填充自己的内容。
  • 可复用 UI 元素 (Modal.vue, Card.vue, Dropdown.vue):

    • 场景: 封装通用的交互和样式,但允许内容高度自定义。
    • 用法 : Modal 组件提供 header (标题), body (内容), footer (按钮) 插槽。
  • 列表渲染器 (DataList.vue, ProductGrid.vue):

    • 场景 : 组件负责获取和迭代数据(如 API 请求、分页),但把如何渲染每一项的控制权交给父组件。
    • 用法 : (核心) 使用作用域插槽 ,将 item (当前项数据) 传递给父组件。这是最灵活的模式。
  • 提供者组件 (Toggle.vue, MouseTracker.vue):

    • 场景 : 组件管理某个状态(如 isOn)或逻辑(如鼠标位置),并通过作用域插槽将这些状态暴露出去,让父组件来决定如何渲染。
    • 用法 : 子组件 <slot :isOn="isOn" :toggle="toggleFunction"></slot>
相关推荐
passerby606114 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了14 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅14 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅15 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅15 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment15 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅16 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊16 小时前
jwt介绍
前端
爱敲代码的小鱼16 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax
吹牛不交税16 小时前
admin.net-v2 框架使用笔记-netcore8.0/10.0版
vue.js·.netcore