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>
相关推荐
杨进军2 小时前
如何实现划词效果
前端·javascript
前端老爷更车2 小时前
esp32 小智AI 项目
前端
destinying2 小时前
五年前端,我凌晨三点的电脑屏幕前终于想通了这件事
前端·javascript·vue.js
想学后端的前端工程师2 小时前
【React Hooks深度实战指南:从原理到最佳实践】
前端·react.js·前端框架
elangyipi1232 小时前
前端面试题:如何减少页面重绘跟重排
前端·面试·html
码界奇点2 小时前
基于Spring Boot和Vue.js的视频点播管理系统设计与实现
java·vue.js·spring boot·后端·spring·毕业设计·源代码管理
想学后端的前端工程师2 小时前
【前端安全防护实战指南:从XSS到CSRF全面防御】
前端·安全·xss
czlczl200209252 小时前
基于 Spring Boot 权限管理 RBAC 模型
前端·javascript·spring boot
未来之窗软件服务2 小时前
幽冥大陆(六十七) PHP5.x SSL 文字加密—东方仙盟古法结界
服务器·前端·ssl·仙盟创梦ide·东方仙盟