Vue3 插槽 <slot>(一文讲透)

Vue3 插槽 的核心概念

Vue3 插槽 是组件化开发中实现内容分发的核心机制。简单来说,它允许父组件向子组件传递任意HTML内容,就像在模板中预留的"接口"。这种设计解决了组件内容动态替换的难题,使开发者能够构建高度可复用的组件模板。例如,按钮组件可以通过插槽插入图标、文字或复杂布局,而无需硬编码内容。

Vue3 插槽 的基础语法

默认插槽

<!-- 父组件 -->
<template>
  <MyComponent>
    <p>这是插入到默认插槽的内容</p>
  </MyComponent>
</template>

<!-- 子组件 MyComponent.vue -->
<template>
  <div class="container">
    <slot>默认内容</slot> <!-- 无内容时显示默认文本 -->
  </div>
</template>

具名插槽

<!-- 父组件 -->
<template>
  <Layout>
    <template #header>
      <h1>页面标题</h1>
    </template>
    <template #default>
      <p>主内容区域</p>
    </template>
    <template #footer>
      <p>页脚信息</p>
    </template>
  </Layout>
</template>

<!-- 子组件 Layout.vue -->
<template>
  <div class="layout">
    <header>
      <slot name="header"></slot>
    </header>
    <main>
      <slot></slot> <!-- 默认插槽 -->
    </main>
    <footer>
      <slot name="footer"></slot>
    </footer>
  </div>
</template>

作用域插槽

<!-- 子组件 Table.vue -->
<template>
  <table>
    <tr>
      <slot name="row" v-for="item in items" :row="item">
        <!-- 默认行模板 -->
        <td>{{ item.id }}</td>
        <td>{{ item.name }}</td>
      </slot>
    </tr>
  </table>
</template>

<!-- 父组件使用 -->
<template>
  <Table :items="dataList">
    <template #row="{ row }">
      <td class="custom-id">{{ row.id }}</td>
      <td class="custom-name">{{ row.name.toUpperCase() }}</td>
    </template>
  </Table>
</template>

Vue3 插槽 的进阶特性

插槽类型 传递方式 适用场景 示例语法
默认插槽 直接传递HTML内容 基础内容注入 <MyComponent>内容</MyComponent>
具名插槽 使用 #slotName 语法 页面布局划分 <template #header>
作用域插槽 通过 :slotProp 传递数据 动态数据展示 #row="{ item }"
传参插槽 v-slot:slotName="data" 复杂数据处理 v-slot:custom="props"

动态插槽命名

<template>
  <DynamicComponent>
    <!-- 通过变量控制插槽名称 -->
    <template [v-slot:dynamicName]="data">
      <p>动态绑定的插槽内容:{{ data.value }}</p>
    </template>
  </DynamicComponent>
</template>

插槽内容作用域

<!-- 子组件传递数据 -->
<template>
  <div>
    <slot :count="100" :text="欢迎使用">
      <!-- 默认内容 -->
    </slot>
  </div>
</template>

<!-- 父组件接收数据 -->
<template>
  <ChildComponent>
    <template v-slot="{ count, text }">
      <p>{{ text }}, 当前计数:{{ count }}</p>
    </template>
  </ChildComponent>
</template>

Vue3 插槽 的实战应用

复用性导航栏组件

<!-- 导航栏组件 Navigation.vue -->
<template>
  <nav>
    <slot name="logo" :logo="appLogo" />
    <slot name="menu" :items="menuItems" />
    <slot name="actions" />
  </nav>
</template>

<!-- 页面A使用 -->
<template>
  <Navigation>
    <template #logo="{ logo }">
      <img :src="logo.url" alt="Logo" width="100" />
    </template>
    <template #menu="{ items }">
      <ul>
        <li v-for="item in items" :key="item.id">{{ item.name }}</li>
      </ul>
    </template>
    <template #actions>
      <button>登录</button>
    </template>
  </Navigation>
</template>

动态内容对话框

<!-- Dialog.vue -->
<template>
  <div class="dialog">
    <header>
      <slot name="title" default="默认标题" />
    </header>
    <main>
      <slot />
    </main>
    <footer>
      <slot name="footer" :close="closeDialog">
        <button @click="closeDialog">关闭</button>
      </slot>
    </footer>
  </div>
</template>

<!-- 使用示例 -->
<template>
  <Dialog :title="自定义标题">
    <p>这是对话框主体内容</p>
    <template #footer="{ close }">
      <button @click="close">确认</button>
      <button @click="close">取消</button>
    </template>
  </Dialog>
</template>

Vue3 插槽 的注意事项

  • 插槽位置混乱:避免将插槽内容放在错误的HTML结构层级中
  • 作用域混淆:作用域插槽传递的props与组件内部变量冲突时,优先使用解构命名
  • 默认内容失效:当父组件传递空内容时,子组件默认内容才会显示
  • 具名插槽覆盖:多个同名插槽定义时,仅最后一个会生效

代码示例中展示了默认插槽、具名插槽和作用域插槽的典型用法。通过<slot>标签配合v-slot指令,可以实现灵活的组件内容定制。实际开发中建议为插槽设置清晰的命名规范,避免将业务逻辑混杂在插槽内容中。