Appearance
✨ 插槽 👌
要点速览
- 插槽用于“父组件传递模板内容”给子组件,传的是结构而非数据。
- 分类:默认插槽与具名插槽(
name),具名可使用v-slot:name或#name。 - 支持动态插槽名:
v-slot:[slotName]或#[slotName]。 - 作用域插槽:子组件在
<slot>上绑定属性,父组件通过v-slot接收(可解构)。 - 作用域规则:父模板只访问父作用域;子模板只访问子作用域。
- 常见模式:无渲染组件(只提供逻辑,视图由插槽决定)。
快速上手
子组件定义默认与具名插槽,父组件填充不同内容:
vue
<!-- components/Card.vue -->
<template>
<div class="card">
<div class="card-header">
<slot name="header"></slot>
</div>
<div class="card-body">
<slot></slot>
</div>
</div>
</template>
<script setup></script>
<style scoped>
.card {
border: 1px solid #ccc;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
width: 300px;
margin: 20px;
}
.card-header {
background-color: #f7f7f7;
border-bottom: 1px solid #ececec;
padding: 10px 15px;
font-size: 16px;
font-weight: bold;
border-top-left-radius: 8px;
border-top-right-radius: 8px;
}
.card-body {
padding: 15px;
font-size: 14px;
color: #333;
}
</style>vue
<!-- 父组件 -->
<template>
<div>
<Card>
<template v-slot:header>我的卡片标题</template>
这是卡片的内容
</Card>
<Card>
<template #header>探险摄影</template>
<div>
<img src="./assets/landscape.jpeg" class="card-image" />
<p>探索未知的自然风光,记录每一个令人惊叹的瞬间。</p>
</div>
</Card>
</div>
</template>
<script setup>
import Card from "./components/Card.vue";
</script>
<style scoped>
.card-image {
width: 100%;
height: auto;
border-bottom: 1px solid #ececec;
}
</style>插槽作用域与渲染
- 插槽内容在父组件作用域中渲染;无法直接访问子组件的数据。
- 子组件只负责“占位”,视图内容由父组件提供并在父作用域计算。
插槽相关细节
默认内容
若父组件未提供内容,默认插槽会渲染其内部默认值:
vue
<slot>这是默认插槽的默认值</slot>具名插槽与简写
vue
<template v-slot:header>探险摄影</template>
<!-- 简写 -->
<template #header>探险摄影</template>当同时接收默认与具名插槽时,位于顶级的非 template 节点的内容会进入默认插槽。
动态插槽名
vue
<template v-slot:[slotName]>探险摄影</template>
<!-- 简写 -->
<template #[slotName]>探险摄影</template>作用域插槽(向上暴露数据)
子组件通过在 <slot> 上绑定属性向父组件暴露数据,父组件以 v-slot 接收:
vue
<!-- 子组件 MyComponent.vue -->
<template>
<div>
<slot :text="greetingMessage" :count="1"></slot>
</div>
</template>vue
<!-- 父组件接收 -->
<MyComponent v-slot="slotProps">
{{ slotProps.text }} {{ slotProps.count }}
</MyComponent>也可在父层解构:
vue
<MyComponent v-slot="{ text, count }">
{{ text }} {{ count }}
</MyComponent>
作用域与响应性
slotProps来自子组件,是按照父作用域进行渲染的只读“输入”。- 一般不在父层直接修改
slotProps,需要交互时使用 Props / 事件 /v-model。
具名作用域插槽
当需要同时暴露多个数据或区分不同区域时,推荐使用具名作用域插槽:
vue
<!-- 子组件:在具名插槽上绑定数据 -->
<template>
<ul>
<li v-for="(item, i) in list" :key="item.id">
<slot name="item" :item="item" :index="i">
<!-- 父组件未提供时的回退内容 -->
{{ item.defaultText }}
</slot>
</li>
</ul>
</template>vue
<!-- 父组件:使用具名作用域插槽并解构接收 -->
<List>
<template #item="{ item, index }">
<span>{{ index + 1 }}. {{ item.name }}</span>
</template>
</List>也支持动态具名作用域插槽:
vue
<!-- 父组件:动态插槽名,并解构接收 -->
<List>
<template v-slot:[slotName]="{ item }">
<strong>{{ item.name }}</strong>
</template>
</List>使用建议
- 具名插槽适合复杂结构或多区域输出(如 header、footer、item)。
- 父层必须用
template包裹具名插槽内容,便于传入复杂结构和解构slot props。 - 保持插槽名一致;
#item与name="item"必须匹配。
实战:列表与无渲染组件
子组件通过具名插槽把每项数据暴露给父组件,父组件决定具体渲染:
vue
<!-- 子组件 List.vue -->
<template>
<div class="list-container">
<ul>
<li v-for="item in items" :key="item.id">
<slot name="item" :item="item">{{ item.defaultText }}</slot>
</li>
</ul>
</div>
</template>
<script setup>
import { ref } from "vue";
const items = ref([
{
id: 1,
name: "Vue.js",
defaultText: "Vue.js 是一个渐进式 JavaScript 框架。",
},
{
id: 2,
name: "React",
defaultText: "React 是一个用于构建用户界面的 JavaScript 库。",
},
{
id: 3,
name: "Angular",
defaultText: "Angular 是一个开源的 Web 应用框架。",
},
]);
</script>
<style>
.list-container {
max-width: 300px;
background: #f9f9f9;
border: 1px solid #ccc;
padding: 20px;
border-radius: 8px;
}
ul {
list-style: none;
padding: 0;
}
li {
margin-bottom: 10px;
background: #fff;
padding: 10px;
border-radius: 6px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
</style>vue
<!-- 父组件使用 -->
<template>
<div class="app-container">
<List>
<template #item="{ item }">
<h3>{{ item.name }}</h3>
<p>{{ item.defaultText }}</p>
</template>
</List>
</div>
</template>
<script setup>
import List from "@/components/List.vue";
</script>
<style>
.app-container {
padding: 20px;
}
</style>无渲染组件
- 有些组件只包含逻辑,不直接输出视图;视图由作用域插槽交给使用者决定。
- 这种模式提高了可复用性和灵活性(列表、表格、图表容器等)。
与 Props / 事件 / v-model 的关系
- 插槽传的是“结构与渲染上下文”,不负责数据双向传输。
- 要传数据用 Props;要向上通知用事件;要受控双向用
v-model。 - 插槽常与 Props 同用:通过 Props 控制可见性/状态,通过插槽定制内容。
常见误区
- 在插槽内容中直接访问子组件数据,会因作用域限制而报错或为空。
- 忘记在父层使用
template包裹具名插槽,导致无法传入复杂结构。 - 将插槽误当作数据通道;应使用 Props / 事件 /
v-model完成数据交互。 - 动态插槽名书写不规范(方括号遗漏),导致插槽无法匹配。
小结与后续
插槽让组件在保持封装的同时具备强大的内容定制能力:
- 先掌握默认/具名/动态插槽的用法与作用域规则。
- 再熟悉作用域插槽,把子组件的数据以插槽 Props 暴露给父层渲染。
- 最后与 Props/事件/
v-model组合,构建灵活的可复用组件。
学习建议
- 建议配合阅读「Props」「自定义事件」「组件 v-model」形成完整心智模型。
- 从一个列表或卡片组件开始实践具名与作用域插槽,逐步提炼为无渲染组件。
