Vue-slot 插槽
一、前言
在 Vue 开发中,我们经常使用组件化思想来构建可复用的 UI 模块。但有时候,我们希望父组件能向子组件“注入”一些自定义内容,比如按钮文字、列表项、甚至是复杂的表单。
这时候,slot(插槽)就派上用场了!
📌 插槽是 Vue 实现“内容分发”(Content Distribution)的核心机制。
本文将带你从零开始,全面掌握 Vue 中的三种插槽:
- ✅ 默认插槽(Default Slot)
- ✅ 具名插槽(Named Slot)
- ✅ 作用域插槽(Scoped Slot)
并通过真实项目案例,让你真正理解插槽的强大与灵活。
二、什么是插槽(slot)?
1. 类比理解
想象一个“卡片组件”:
<Card><h3>标题</h3><p>这里是内容</p><button>操作按钮</button>
</Card>我们希望 Card 组件能“接收”父组件传入的这些内容,并把它们渲染到正确的位置。
插槽就是“预留的坑位”,父组件可以往这些“坑”里填内容。
2. 插槽的本质
- 插槽是 Vue 提供的一种内容分发 API
- 它允许父组件向子组件传递 HTML 模板片段
- 子组件通过
<slot></slot>标签定义“内容插入点”
💡 插槽不是 prop,它传递的是“结构”而非“数据”。
三、默认插槽(Default Slot)
最简单的插槽,用于传递单一内容。
1. 子组件:Card.vue
<template><div class="card" style="border: 1px solid #ddd; padding: 20px; margin: 10px;"><slot></slot> <!-- 默认插槽:内容将插入到这里 --></div>
</template><script>
export default {name: 'Card'
}
</script>2. 父组件使用
<template><Card><h3>欢迎使用插槽</h3><p>这是默认插槽的内容</p></Card>
</template><script>
import Card from './components/Card.vue'
export default {components: { Card }
}
</script>✅ 渲染结果:
<div class="card"><h3>欢迎使用插槽</h3><p>这是默认插槽的内容</p>
</div>四、具名插槽(Named Slot)
当组件需要多个“内容插入点”时,使用具名插槽。
1. 子组件:Layout.vue
<template><div class="layout"><header><slot name="header"></slot></header><main><slot></slot> <!-- 默认插槽 --></main><footer><slot name="footer"></slot></footer></div>
</template><script>
export default {name: 'Layout'
}
</script><style>
.layout header, .layout footer {background: #f1f1f1;padding: 10px;text-align: center;
}
.layout main {padding: 20px;
}
</style>2. 父组件使用
<template><Layout><!-- 具名插槽:使用 v-slot:header --><template v-slot:header><h1>网站标题</h1></template><!-- 默认插槽 --><p>这里是主要内容区域...</p><!-- 具名插槽:使用 v-slot:footer --><template v-slot:footer><small>© 2025 版权所有</small></template></Layout>
</template><script>
import Layout from './components/Layout.vue'
export default {components: { Layout }
}
</script>💡
v-slot:header也可以简写为#header(推荐):
<template #header><h1>网站标题</h1>
</template>五、作用域插槽(Scoped Slot)—— 核心难点!
有时候,父组件想使用子组件内部的数据来渲染内容,这就是作用域插槽的用武之地。
1. 子组件:UserList.vue
<template><ul><li v-for="user in users" :key="user.id"><!-- 将 user 数据通过插槽传递出去 --><slot :user="user" :index="index"><!-- 默认内容 -->{{ user.name }}</slot></li></ul>
</template><script>
export default {name: 'UserList',data() {return {users: [{ id: 1, name: '张三', age: 25 },{ id: 2, name: '李四', age: 30 },{ id: 3, name: '王五', age: 28 }]}}
}
</script>2. 父组件:自定义渲染逻辑
<template><UserList><!-- 接收子组件传递的数据 --><template v-slot:default="slotProps"><strong>{{ slotProps.user.name }}</strong>({{ slotProps.user.age }}岁)- 第 {{ slotProps.index + 1 }} 位</template></UserList>
</template><script>
import UserList from './components/UserList.vue'
export default {components: { UserList }
}
</script>✅ 渲染结果:
<ul><li><strong>张三</strong> (25岁) - 第 1 位</li><li><strong>李四</strong> (30岁) - 第 2 位</li><li><strong>王五</strong> (28岁) - 第 3 位</li>
</ul>🔍 关键点解析:
v-slot:default="slotProps":接收插槽传递的数据slotProps是一个对象,包含{ user, index }- 你也可以解构写法:
v-slot="{ user, index }"
六、插槽的简写语法(推荐)
| 原写法 | 简写 |
|---|---|
v-slot:header | #header |
v-slot:default="data" | #default="data" 或 v-slot="data"(默认插槽) |
✅ 推荐使用 # 语法,更简洁:
<UserList><template #default="{ user }"><span>{{ user.name }}</span></template>
</UserList>七、插槽的实际应用场景
| 场景 | 说明 |
|---|---|
| UI 组件库 | Button、Modal、Table 等组件通过插槽支持自定义内容 |
| 列表渲染 | Table 组件使用作用域插槽自定义列内容 |
| 布局组件 | Header、Sidebar、Footer 通过具名插槽构建页面结构 |
| 高阶组件 | 封装通用逻辑,通过插槽暴露定制能力 |
八、最佳实践与注意事项
✅ 推荐做法
- 使用
#语法替代v-slot:xxx - 为插槽提供默认内容,提高组件健壮性
- 作用域插槽中传递的数据尽量精简明确
- 在组件文档中清晰说明插槽用法
❌ 避免踩坑
- 不要在插槽中直接修改子组件数据
- 注意插槽作用域,避免变量冲突
- Vue 2 中
v-slot只能用在<template>或组件上
九、Vue 2 vs Vue 3 插槽差异
| 特性 | Vue 2 | Vue 3 |
|---|---|---|
| 插槽语法 | slot="xxx"、slot-scope | 统一为 v-slot |
| 作用域插槽 | slot-scope | v-slot |
| 兼容性 | 支持旧语法 | 仅支持 v-slot |
📌 建议:统一使用 Vue 3 的
v-slot语法,更规范。
十、总结
| 插槽类型 | 适用场景 | 语法 |
|---|---|---|
| 默认插槽 | 单一内容插入 | <slot></slot> |
| 具名插槽 | 多个插入点 | v-slot:header 或 #header |
| 作用域插槽 | 父组件使用子组件数据 | v-slot="{ data }" |
✅ 一句话总结:
插槽 = 组件的“留白艺术”,让组件更灵活、更可复用。
十一、结语
感谢您的阅读!如果你有任何疑问或想要分享的经验,请在评论区留言交流!
