Vue 插槽的三种用法
默认插槽
插槽是 Vue 组件的一个基础特性,让父组件可以向子组件的指定位置注入内容。子组件用 <slot> 标签声明一个占位区,父组件在子组件标签内写入的内容会替换这个占位区。
vue
<!-- 子组件 CardPanel.vue -->
<template>
<div class="card">
<h3>{{ title }}</h3>
<slot />
</div>
</template>
<!-- 父组件 -->
<CardPanel title="用户列表">
<p>这是卡片内部的动态内容</p>
</CardPanel><slot> 中可以指定默认内容——如果父组件没有传入任何内容,则显示后备内容:
vue
<slot>没有传入内容时的默认展示</slot>具名插槽
一个组件内部可能需要多个插槽位置,比如页面布局有头部、主体和底部三个区域。具名插槽用 name 属性区分不同的插入点:
vue
<!-- 子组件 LayoutPanel.vue -->
<template>
<div class="layout">
<header>
<slot name="header" />
</header>
<main>
<slot />
</main>
<footer>
<slot name="footer" />
</footer>
</div>
</template>父组件使用 v-slot: 或缩写 # 来指定向哪个插槽注入内容:
vue
<LayoutPanel>
<template #header>
<h1>页面标题</h1>
</template>
<p>主体内容区域</p>
<template #footer>
<span>版权信息 © 2026</span>
</template>
</LayoutPanel>没有显式命名的内容会自动填充到默认插槽(即没有 name 属性的 <slot>)。
作用域插槽
作用域插槽让子组件向父组件暴露内部数据,父组件根据这些数据决定如何渲染。
数据流向是:子组件通过 :属性名 将数据抛出,父组件通过 v-slot 接收。相比 props 的"父传子",作用域插槽实现了"子传数据,父定渲染"的模式。
vue
<!-- 子组件 ProductList.vue -->
<template>
<ul>
<li v-for="item in productList" :key="item.id">
<slot name="item" :product="item" :index="index" />
</li>
</ul>
</template>
<script setup>
const productList = [
{ id: 1, name: '商品A', price: 99 },
{ id: 2, name: '商品B', price: 199 }
]
</script>
<!-- 父组件 -->
<ProductList>
<template #item="{ product, index }">
<span>{{ index + 1 }}. {{ product.name }} - ¥{{ product.price }}</span>
</template>
</ProductList>作用域插槽是组件封装和渲染逻辑分离的重要手段。它常用于列表组件(渲染逻辑交给父组件)、表格组件(自定义列)、弹窗组件(自定义内容),让组件保持通用性的同时不失灵活性。
插槽不是 props 的替代
初看之下,插槽和 props 都能把信息从外部传入组件,但它们的职责不同:
- props 传递的是数据,组件内部决定如何渲染
- 插槽 传递的是渲染结构,组件内部决定"放在哪里",外部决定"长什么样"
当组件需要让使用者自定义某个区域的 DOM 结构时,用插槽;当组件需要配置行为或数据时,用 props。两者搭配使用才是正确的组件设计方式。