3. 创建第一个自定义槽位组件

本章将引导你从零搭建一个带自定义槽位的 BottomNavigation 组件。你将在 Vue 3 + Vite 环境下完成项目初始化、组件注册(全局与局部)以及编写支持默认 slot 和命名 slot 的模板结构。槽位的核心价值在于将“布局占位”与“内容实现”解耦——组件只声明几个空孔位,父组件通过 slot 注入图标、文字、徽标等任意内容。下面按三步完成。

学习前提:确保已安装 Node.js 18+ 和 npm/pnpm。

3.1 项目初始化

使用 Vite 快速创建 Vue 3 + TypeScript 项目:

npm create vite@latest bottom-nav-slot -- --template vue-ts
cd bottom-nav-slot
npm install

安装完成后,进入 src/components/ 目录,创建 BottomNavigation.vue 文件。项目文件夹结构如下:

src/
├── components/
│   ├── BottomNavigation.vue    ← 待创建
│   └── HelloWorld.vue
├── App.vue
├── main.ts
└── ...

3.2 组件注册

BottomNavigation 建议以局部注册方式在父组件中使用,更灵活且便于单元测试。在 App.vue 中:

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

<template>
  <BottomNavigation>
    <!-- 内容通过 slot 注入 -->
  </BottomNavigation>
</template>

若需要全局注册(例如在插件中),可在 main.ts 中添加:

import { createApp } from 'vue'
import App from './App.vue'
import BottomNavigation from './components/BottomNavigation.vue'

const app = createApp(App)
app.component('BottomNavigation', BottomNavigation)
app.mount('#app')
注册方式作用域适用场景
局部注册当前组件按需引入、树摇优化、测试隔离
全局注册所有组件组件库、全局导航、频繁复用

3.3 基本模板结构

在 BottomNavigation.vue 中编写模板,暴露三个槽位:默认 slot(主内容)和两个命名 slot icon、badge。组件只提供 flex 布局容器和语义结构,不渲染具体内容。

<template>
  <nav class="bottom-nav">
    <div class="nav-item" v-for="item in items" :key="item.key">
      <!-- 图标槽位,默认显示占位 -->
      <div class="icon-wrapper">
        <slot name="icon" :item="item">
          <span>📦</span>
        </slot>
      </div>

      <!-- 徽标槽位(可选) -->
      <div class="badge-wrapper">
        <slot name="badge" :item="item" />
      </div>

      <!-- 默认槽位,通常用于标签文本 -->
      <div class="label-wrapper">
        <slot :item="item">
          <span>{{ item.label }}</span>
        </slot>
      </div>
    </div>
  </nav>
</template>

<script setup lang="ts">
defineProps<{
  items: Array<{ key: string; label: string }>
}>()
</script>

<style scoped>
.bottom-nav {
  display: flex;
  justify-content: space-around;
  border-top: 1px solid #ddd;
  padding: 8px 0;
}
.nav-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
}
.icon-wrapper, .badge-wrapper, .label-wrapper {
  display: flex;
  align-items: center;
  justify-content: center;
}
.badge-wrapper {
  position: absolute;
  top: -4px;
  right: -8px;
}
</style>

这段模板展示了三个槽位如何分工:命名 slot 接收特定内容(图标、徽标),默认 slot 接收未显式指定 name 的内容(标签文本)。每个 slot 都绑定了 :item 作用域变量,父组件可通过 slot-scope 访问导航项数据。

常见错误:如果没有为命名 slot 提供回退内容(如 <span>📦</span>),当父组件未填充时,该区域会渲染为空,可能破坏布局。务必为每个 slot 设计合理的占位内容。
槽位原理回顾:Vue 的 slot 机制本质是“编译时内容分发”。组件在模板中用 <slot> 声明孔位,父组件在使用时通过模板内的子节点填充这些孔位。命名 slot 则通过 v-slot:name 定向填充。下图展示了本节创建的组件结构。
BottomNavigation 组件结构与槽位示意图 父组件模板 (App.vue) <BottomNavigation> 组件 slot name="icon" slot name="badge" 默认 slot 自定义图标 自定义徽标 自定义标签 父组件通过 v-slot:icon / v-slot:badge / v-slot(默认)传入自定义内容

至此,你已成功创建了第一个包含自定义槽位的 BottomNavigation 组件。接下来的章节将在此基础上深入作用域 slot、样式覆盖、事件传递等进阶技巧。