Vue3 入门教程
插槽(上)
本教程共 40 篇 · 第 17 篇 · 更新于 2026-07-29 · 约 7 分钟阅读
VueVue3 入门教程插槽Slot命名插槽作用域
17. 插槽(上)
本节目标:理解插槽的概念和基础用法,学会用默认插槽和命名插槽让组件更灵活。
插槽解决了什么问题
Props 能传数据,事件能传消息。但如果你想传一段 HTML 结构呢?
比如一个 <FancyButton> 组件,外层样式固定,但内部文字或图标由使用者决定。这时候就需要插槽。
插槽(Slot)就是组件内部的”占位符”(Placeholder)——父组件往里面填充内容,子组件决定在哪里渲染。
默认插槽
子组件里放一个 <slot> 标签作为出口:
<!-- FancyButton.vue -->
<template>
<button class="fancy-btn">
<!-- 插槽出口:父组件的内容会渲染在这里 -->
<slot></slot>
</button>
</template>
父组件把内容塞进去:
<FancyButton>
Click me!
</FancyButton>
渲染结果:
<button class="fancy-btn">Click me!</button>
用 JavaScript 函数来类比:
// 父组件调用
FancyButton('Click me!')
// 子组件渲染
function FancyButton(slotContent) {
return `<button class="fancy-btn">${slotContent}</button>`
}
插槽内容可以是任何模板——文本、元素、甚至其他组件:
<FancyButton>
<span style="color:red">Click me!</span>
<AwesomeIcon name="plus" />
</FancyButton>
后备内容
如果父组件没传内容,<slot> 里可以放默认值:
<!-- SubmitButton.vue -->
<button type="submit">
<!-- 后备内容:父组件没传内容时显示 "Submit" -->
<slot>Submit</slot>
</button>
使用时:
<!-- 没传内容,显示 "Submit" -->
<SubmitButton />
<!-- 传了内容,显示 "Save" -->
<SubmitButton>Save</SubmitButton>
Tip后备内容为空时才显示。如果传了空格或空字符串,后备内容不会渲染。
命名插槽(Named Slots)
一个组件可以有多个插槽,用 name 区分:
<!-- BaseLayout.vue -->
<div class="container">
<header>
<!-- 命名插槽:父组件通过 #header 指定内容 -->
<slot name="header"></slot>
</header>
<main>
<!-- 默认插槽(default):未指定名字的内容会到这里 -->
<slot></slot>
</main>
<footer>
<!-- 命名插槽:父组件通过 #footer 指定内容 -->
<slot name="footer"></slot>
</footer>
</div>
父组件用 <template v-slot:name> 指定内容去向。v-slot 有简写 #:
<BaseLayout>
<template #header>
<h1>页面标题</h1>
</template>
<template #default>
<p>主要内容区域</p>
</template>
<template #footer>
<p>页脚信息</p>
</template>
</BaseLayout>
没写名字的 <template> 或直接在组件标签下的内容,会进入默认插槽:
<BaseLayout>
<template #header>
<h1>页面标题</h1>
</template>
<!-- 以下都是默认插槽的内容 -->
<p>段落一</p>
<p>段落二</p>
</BaseLayout>
Note组件标签下所有非
<template>的顶级节点,自动归入默认插槽。
渲染作用域(Render Scope)
插槽内容在父组件里定义,所以它能访问父组件的数据,不能访问子组件的数据:
<!-- ✅ 能访问父组件的 message(插槽内容继承父组件作用域) -->
<FancyButton>{{ message }}</FancyButton>
<!-- ❌ 不能访问子组件内部的数据 -->
这就像函数的作用域——定义在哪,就只能访问哪里的变量。
条件插槽(Conditional Slots)
有时候你想判断某个插槽是否传了内容,可以用 $slots:
<template>
<div class="card">
<!-- 判断 header 插槽是否有内容,有才渲染外层 -->
<div v-if="$slots.header" class="card-header">
<slot name="header"></slot>
</div>
<!-- 判断 default 插槽是否有内容,有才渲染外层 -->
<div v-if="$slots.default" class="card-content">
<slot></slot>
</div>
<!-- 判断 footer 插槽是否有内容,有才渲染外层 -->
<div v-if="$slots.footer" class="card-footer">
<slot name="footer"></slot>
</div>
</div>
</template>
这样只有传了内容的位置才会渲染外层包装。
动态插槽名
v-slot 支持动态参数:
<base-layout>
<template #[dynamicSlotName]>
动态指定的插槽
</template>
</base-layout>
Warning动态参数有语法约束——空格和引号不允许出现在表达式里。
本节回顾
- 插槽是组件内部的占位符,让父组件填充内容
<slot>是出口,没名字的叫默认插槽- 命名插槽用
name属性区分,父组件用#name指定 - 插槽内容只能访问父组件的作用域
- 后备内容为空插槽提供默认值