Vue插槽(Slots)详解
文章目录
- 1. 插槽简介
- 1.1 什么是插槽?
- 1.2 为什么需要插槽?
- 1.3 插槽的基本语法
- 2. 默认插槽
- 2.1 什么是默认插槽?
- 2.2 默认插槽语法
- 2.3 插槽默认内容
- 2.4 默认插槽实例:创建一个卡片组件
- 2.5 Vue 3中的默认插槽
- 2.6 默认插槽的应用场景
- 3. 具名插槽
- 3.1 什么是具名插槽?
- 3.2 具名插槽语法
- 3.3 具名插槽缩写
- 3.4 默认插槽的显式名称
- 3.5 具名插槽实例:页面布局组件
- 3.6 Vue 3中的具名插槽
- 3.7 具名插槽的应用场景
- 3.8 动态插槽名
- 4. 作用域插槽
- 4.1 什么是作用域插槽?
- 4.2 为什么需要作用域插槽?
- 4.3 作用域插槽语法
- 4.4 解构插槽Props
- 4.5 作用域插槽实例:自定义列表渲染
- 4.6 作用域插槽与具名插槽结合
- 4.7 Vue 3中的作用域插槽
- 4.8 作用域插槽的应用场景
- 5. 插槽高级用法
- 5.1 渲染函数中的插槽
- 5.2 插槽包装器模式
- 5.3 函数式组件中的插槽
- 5.4 递归插槽
- 5.5 透传插槽
- 5.6 插槽与v-for结合
- 6. 插槽最佳实践与常见问题
- 6.1 插槽最佳实践
- 6.2 常见问题与解决方案
- 6.3 Vue 3中的插槽新特性
- 7. 总结
1. 插槽简介
1.1 什么是插槽?
插槽(Slots)是Vue提供的一种内容分发机制,允许我们向组件内部传递内容。通俗地说,插槽就像是组件中的"占位符",你可以在使用组件时,在这个"占位符"中填充任何你想要显示的内容。
插槽使组件变得更加灵活和可复用,因为它允许使用者决定组件内部的部分内容。
1.2 为什么需要插槽?
想象一下,如果没有插槽,当我们需要创建一个按钮组件,可能会出现这样的情况:
// 没有插槽的按钮组件
Vue.component('my-button', {template: '<button class="btn">点击我</button>'
})
这个组件只能显示"点击我"这个文本。如果我们想要显示其他文本,就需要通过属性传递:
Vue.component('my-button', {props: ['text'],template: '<button class="btn">{{ text }}</button>'
})
<my-button text="保存"></my-button>
<my-button text="取消"></my-button>
但是,如果我们想要按钮内部显示复杂的HTML结构(如图标+文字),上面的方法就不够灵活了。这时插槽就派上用场了:
Vue.component('my-button', {template: '<button class="btn"><slot></slot></button>'
})
<my-button><i class="icon-save"></i> 保存
</my-button><my-button><i class="icon-cancel"></i> 取消
</my-button>
通过插槽,我们可以在不修改组件本身的情况下,灵活地决定组件内容。
1.3 插槽的基本语法
在Vue中,插槽使用<slot></slot>
标签定义。一个简单的带插槽的组件示例如下:
Vue.component('alert-box', {template: `<div class="alert-box"><strong>重要提示!</strong><slot></slot></div>`
})
使用该组件时,我们可以在组件标签内添加内容,这些内容将替换组件模板中的<slot></slot>
部分:
<alert-box>您的账号已被锁定,请联系管理员。
</alert-box>
渲染结果:
<div class="alert-box"><strong>重要提示!</strong>您的账号已被锁定,请联系管理员。
</div>
2. 默认插槽
2.1 什么是默认插槽?
默认插槽是最基本的插槽类型,就是我们上面看到的例子。它不需要名字,是组件中的默认内容分发位置。
2.2 默认插槽语法
使用<slot></slot>
标签定义一个默认插槽:
Vue.component('my-component', {template: `<div><h2>组件标题</h2><slot></slot></div>`
})
使用该组件:
<my-component><p>这是一些默认插槽内容</p><p>可以有多个元素</p>
</my-component>
渲染结果:
<div><h2>组件标题</h2><p>这是一些默认插槽内容</p><p>可以有多个元素</p>
</div>
2.3 插槽默认内容
有时候,我们希望在使用者没有提供内容时,插槽能显示一些默认内容。这时,我们可以在<slot>
标签内部添加内容作为默认值:
Vue.component('submit-button', {template: `<button type="submit"><slot>提交</slot></button>`
})
使用该组件:
<!-- 使用默认内容 -->
<submit-button></submit-button><!-- 替换默认内容 -->
<submit-button>保存</submit-button>
渲染结果:
<!-- 使用默认内容的渲染结果 -->
<button type="submit">提交</button><!-- 替换默认内容的渲染结果 -->
<button type="submit">保存</button>
2.4 默认插槽实例:创建一个卡片组件
让我们创建一个更实用的例子 - 一个卡片组件,它有标题和内容区域,其中内容区域使用默认插槽:
Vue.component('card', {props: ['title'],template: `<div class="card"><div class="card-header"><h3>{{ title }}</h3></div><div class="card-body"><slot>没有内容提供</slot></div></div>`
})
使用该组件:
<card title="欢迎"><p>感谢您访问我们的网站!</p><button>开始探索</button>
</card><card title="提示"></card>
渲染结果:
<div class="card"><div class="card-header"><h3>欢迎</h3></div><div class="card-body"><p>感谢您访问我们的网站!</p><button>开始探索</button></div>
</div><div class="card"><div class="card-header"><h3>提示</h3></div><div class="card-body">没有内容提供</div>
</div>
2.5 Vue 3中的默认插槽
在Vue 3中,默认插槽的使用方式与Vue 2基本相同,但是组件的定义方式可能不同:
// Vue 3使用setup语法
const Card = {props: ['title'],template: `<div class="card"><div class="card-header"><h3>{{ title }}</h3></div><div class="card-body"><slot>没有内容提供</slot></div></div>`
}
使用组合式API (Composition API):
import { defineComponent } from 'vue'export default defineComponent({props: ['title'],setup(props) {// 此处是组合式API的逻辑return () => (<div class="card"><div class="card-header"><h3>{props.title}</h3></div><div class="card-body">{/* JSX中的插槽表示 */}{this.$slots.default?.() || '没有内容提供'}</div></div>)}
})
2.6 默认插槽的应用场景
默认插槽非常适合以下场景:
- 包装组件 - 当你需要在某些内容周围添加一致的样式或结构
- 布局组件 - 例如容器、卡片或面板
- 功能性组件 - 如模态框、警告框,其中内容可变但行为一致
例如,一个简单的模态框组件:
Vue.component('modal-dialog', {props: ['isOpen'],template: `<div v-if="isOpen" class="modal-overlay"><div class="modal"><div class="modal-header"><button @click="$emit('close')" class="close-btn">×</button></div><div class="modal-body"><slot>这是模态框的默认内容</slot></div></div></div>`
})
使用该组件:
<modal-dialog :is-open="showModal" @close="showModal = false"><h2>重要通知</h2><p>您的订单已经成功提交!</p><button @click="showModal = false">确定</button>
</modal-dialog>
3. 具名插槽
3.1 什么是具名插槽?
当我们需要在组件中定义多个插槽时,就需要使用具名插槽。具名插槽允许我们将不同的内容分发到组件模板的不同位置。
例如,一个典型的页面布局组件可能需要头部、侧边栏、主内容区和底部等多个插槽。
3.2 具名插槽语法
在Vue 2.6之前,具名插槽使用slot
属性:
Vue.component('layout', {template: `<div class="container"><header><slot name="header"></slot></header><main><slot></slot></main><footer><slot name="footer"></slot></footer></div>`
})
<layout><h1 slot="header">网站标题</h1><p>主内容区域</p><p slot="footer">版权信息 © 2023</p>
</layout>
在Vue 2.6及以后的版本中,引入了v-slot
指令,替代了slot
属性:
<layout><template v-slot:header><h1>网站标题</h1></template><p>主内容区域</p><template v-slot:footer><p>版权信息 © 2023</p></template>
</layout>
注意:在Vue 2.6以后,
v-slot
指令只能用在<template>
标签上(除了一种特殊情况,后面会讲到)。
3.3 具名插槽缩写
v-slot
指令可以缩写为#
,这使得模板更加简洁:
<layout><template #header><h1>网站标题</h1></template><p>主内容区域</p><template #footer><p>版权信息 © 2023</p></template>
</layout>
3.4 默认插槽的显式名称
默认插槽其实有一个隐含的名称default
。所以以下两种写法是等价的:
<!-- 隐式默认插槽 -->
<layout><p>主内容区域</p>
</layout><!-- 显式默认插槽 -->
<layout><template #default><p>主内容区域</p></template>
</layout>
3.5 具名插槽实例:页面布局组件
让我们创建一个更完整的页面布局组件:
Vue.component('page-layout', {template: `<div class="page"><header class="page-header"><slot name="header"><h1>默认标题</h1></slot></header><nav class="page-sidebar"><slot name="sidebar"><ul><li>默认导航项1</li><li>默认导航项2</li></ul></slot></nav><main class="page-content"><slot></slot></main><footer class="page-footer"><slot name="footer"><p>默认