目录
Vue 3 插槽继承使用方法示例(基于 Ant Design Vue)
实例演示
背景
问题
解决方案:插槽继承
如何解决
插槽继承的原理
实现步骤
1. 子组件:封装 EaAutoComplete.vue
说明:
2. 父组件:自定义插槽内容
说明:
3. 调试:对比原组件与新组件的插槽继承效果
编辑
说明:
总结
Vue 3 slot/插槽继承使用方法示例(基于 Ant Design Vue)
实例演示
如图新组件继承自原组件,且可以使用原组件的插槽。示例组件为 a-auto-complete
背景
在开发 Vue 3 应用时,尤其是在使用 Ant Design Vue 等 UI 组件库时,我们经常需要封装和自定义第三方组件。为了使组件更加灵活和可重用,我们希望父组件能够灵活地控制子组件的内容,而不需要修改子组件的代码。Vue 的插槽继承机制提供了一个有效的解决方案,它使得父组件能够通过插槽动态传递内容给子组件,从而大大提高了组件的灵活性和复用性。
问题
- 父子组件之间的内容传递问题:子组件需要展示不同的数据和结构,但不希望每次修改子组件时都需要改动父组件。
- 自定义插槽内容:当父组件需要定制子组件的插槽内容时,如何避免直接修改子组件代码,且保持灵活性和可扩展性?
解决方案:插槽继承
Vue 3 提供了插槽继承机制,通过 $slots
和 v-bind="$attrs"
可以让子组件动态接受并渲染父组件的插槽内容。这种方式避免了对子组件的硬编码修改,增强了组件的复用性和灵活性。
如何解决
- 封装子组件:子组件通过
v-bind="$attrs"
将父组件传递的所有属性(包括插槽)传递给 Ant Design Vue 的a-auto-complete
组件,同时使用$slots
机制来动态渲染父组件传递的插槽内容。 - 父组件自定义插槽:父组件通过
#slotName
的方式传递插槽内容,自定义渲染方式,而无需对子组件进行改动。
插槽继承的原理
- 动态传递插槽:子组件不需要显式声明每个插槽内容,父组件只需要通过插槽名称来传递内容。子组件使用
$slots
来接收父组件传递的插槽数据,并通过v-bind="$attrs"
将父组件的属性传递给实际的原生组件(如a-auto-complete
)。 v-for
循环插槽:通过v-for="(_, name) in $slots"
遍历父组件传递的插槽内容,子组件可以动态地渲染这些内容。这使得子组件能够无缝地继承并显示父组件传递的内容。
实现步骤
1. 子组件:封装 EaAutoComplete.vue
在子组件中封装 Ant Design Vue 的 a-auto-complete
组件,并使用插槽继承机制,将父组件传递的插槽内容动态渲染。
<script lang="ts" setup>
const props = defineProps({
options: {
type: Array,
default: () => [{
label: 'Option 1',
value: 1,
}, {
label: 'Option 2',
value: 2,
}],
},
})
const modelValue = defineModel<string, number>('value')
</script>
<template>
<a-auto-complete
v-model:value="modelValue"
v-bind="$attrs"
:options="props.options"
placeholder="请输入"
>
<!-- 动态传递父组件插槽内容 -->
<template v-for="(_, name) in $slots" #[name]="slotData">
<slot :name="name" v-bind="slotData || {}" />
</template>
</a-auto-complete>
</template>
说明:
- 使用
v-bind="$attrs"
将父组件传递的所有属性(包括v-model
和自定义属性)传递给a-auto-complete
组件。 - 使用
v-for="(_, name) in $slots"
遍历并渲染父组件传递的插槽内容。<slot :name="name" v-bind="slotData || {}" />
将插槽内容传递给父组件,使得父组件能够灵活控制插槽的展示内容。
2. 父组件:自定义插槽内容
父组件可以通过插槽来控制子组件的显示方式。这里自定义了 #option
插槽,来渲染每个选项的名称和其他信息。
<script lang="ts" setup>
import EaAutoComplete from '~/components/EaAutoComplete.vue'
const props = defineProps({
options: {
type: Array,
default: () => [{
label: 'Option 1x',
value: 1,
name: 'Option 1x',
}, {
label: 'Option 2x',
value: 2,
name: 'Option 2x',
}],
},
})
const modelValue = defineModel<string, number>('value')
</script>
<template>
<EaAutoComplete
v-model:value="modelValue"
v-bind="$attrs"
:options="props.options"
placeholder="请输入"
>
<!-- 自定义插槽:控制选项的展示方式 -->
<template #option="item">
<div style="display: flex; flex-direction: column;">
<span> {{ item.name }} </span>
<span style="color: #8c8c8c; font-size: 12px;">{{ String(item.value) }}</span>
</div>
</template>
</EaAutoComplete>
</template>
说明:
- 在父组件中,使用
EaAutoComplete
并通过#option
插槽自定义了每个选项的展示方式。 - 父组件控制了选项内容的布局和显示,不需要对子组件
EaAutoComplete
进行任何修改。
3. 调试:对比原组件与新组件的插槽继承效果
通过调试组件,可以对比 EaAutoComplete
和 Ea2AutoComplete
组件的效果。新组件通过插槽继承可以更灵活地自定义插槽内容。
<script setup lang="ts">
import EaAutoComplete from '~/components/EaAutoComplete.vue'
import Ea2AutoComplete from '~/components/Ea2AutoComplete.vue'
</script>
<template>
<div style="display: flex;">
<div>
<p>原组件</p>
<EaAutoComplete style="width: 200px; margin-right: 20px" />
</div>
<div>
<p>新组件</p>
<Ea2AutoComplete style="width: 200px; margin-right: 20px" />
</div>
</div>
</template>
说明:
DebugComponent
展示了原始组件与新组件的对比。通过插槽继承机制,Ea2AutoComplete
可以动态接收并渲染父组件传递的插槽内容。
总结
- 动态插槽传递:Vue 3 的插槽继承机制允许子组件动态接收并渲染父组件传递的插槽内容,无需显式声明每个插槽。
- 提高组件复用性和灵活性:父组件可以通过插槽灵活地控制子组件的显示内容,而无需对子组件进行修改。插槽继承提高了组件的复用性,使得父组件可以独立修改插槽内容而不影响子组件。
- 降低耦合度:插槽继承降低了父子组件之间的耦合度,父组件可以根据需要自定义插槽内容,而不需要对子组件进行更改。
这种方式特别适用于需要高度定制化和灵活性的 UI 组件开发,尤其是在使用像 Ant Design Vue 这样的第三方组件库时,可以通过插槽继承来实现个性化的 UI 设计,同时保持组件的可复用性和灵活性。