在Vue 2中,函数式组件(functional component) 本身没有实例,只能通过 render 函数接收 context 参数来处理插槽。要实现动态插槽,需要结合 context.slots() 方法和动态插槽名称来灵活分发内容。以下是详细实现方案:
一、核心概念回顾
-
函数式组件特点
- 无状态(无 data、computed)、无实例(无 this)
- 依赖 context 参数获取 props、children、slots 等
- 性能更高,适合纯展示型组件
-
动态插槽需求
- 插槽名称不固定(如根据 props 或外部数据动态生成)
- 需灵活控制插槽内容的分发逻辑
二、基础实现:动态插槽分发
通过 context.slots() 方法可以获取父组件传递的插槽,结合动态名称实现分发。
1. 子组件(函数式组件)
arduino
// DynamicFunctionalSlot.js
export default {
functional: true,
props: {
// 动态插槽名称(由父组件传递)
slotName: {
type: String,
required: true
}
},
render(h, context) {
const { props, slots } = context;
// 获取父组件传递的指定插槽内容
const dynamicSlotContent = slots()[props.slotName];
return h('div', [
// 静态插槽
h('div', { class: 'header' }, slots().header),
// 动态插槽
h('div', { class: 'dynamic-content' }, dynamicSlotContent),
// 默认插槽
h('div', { class: 'footer' }, slots().default)
]);
}
};
2. 父组件使用
xml
<template>
<div>
<DynamicFunctionalSlot :slot-name="currentSlot">
<!-- 静态插槽:header -->
<template slot="header">
<h1>固定头部</h1>
</template>
<!-- 动态插槽候选:slotA、slotB -->
<template slot="slotA">
<p>动态内容 A</p>
</template>
<template slot="slotB">
<p>动态内容 B</p>
</template>
<!-- 默认插槽 -->
<p>默认底部内容</p>
</DynamicFunctionalSlot>
<!-- 切换动态插槽 -->
<button @click="currentSlot = 'slotA'">显示插槽A</button>
<button @click="currentSlot = 'slotB'">显示插槽B</button>
</div>
</template>
<script>
import DynamicFunctionalSlot from './DynamicFunctionalSlot';
export default {
components: { DynamicFunctionalSlot },
data() {
return {
currentSlot: 'slotA' // 默认显示slotA
};
}
};
</script>
三、高级场景:动态生成插槽
如果需要动态生成多个插槽(如根据数组循环创建),可以在父组件中通过 v-for 动态绑定插槽名称,子组件则遍历 context.slots() 来分发。
1. 父组件动态传递插槽
xml
<template>
<DynamicFunctionalSlot>
<!-- 动态生成3个插槽:item-0、item-1、item-2 -->
<template v-for="(item, index) in items" :slot="`item-${index}`">
<div>动态插槽 {{ index }}:{{ item.content }}</div>
</template>
</DynamicFunctionalSlot>
</template>
<script>
export default {
data() {
return {
items: [
{ content: '内容A' },
{ content: '内容B' },
{ content: '内容C' }
]
};
}
};
</script>
2. 子组件遍历动态插槽
javascript
// DynamicFunctionalSlot.js
export default {
functional: true,
render(h, context) {
const { slots } = context;
const slotKeys = Object.keys(slots()); // 获取所有插槽名称
return h('div', [
// 遍历所有插槽并渲染
slotKeys.map(key =>
h('div', { class: `slot-item slot-${key}` }, slots()[key])
)
]);
}
};
四、关键API:context.slots()
在函数式组件中,context.slots() 是处理插槽的核心方法:
- slots() 返回值:一个对象,键为插槽名称(如 header、default),值为插槽内容的虚拟DOM数组。
- 动态插槽名称:通过 props 或外部数据动态指定插槽名称(如 slots()[dynamicName])。
- 默认插槽:slots().default 对应父组件未指定名称的内容。
五、注意事项
- 函数式组件无实例:无法使用 this.$slots,必须通过 context.slots() 获取插槽。
- 插槽名称大小写:Vue 2 中插槽名称不区分大小写,但建议统一使用小写(如 slot-a 而非 slotA)。
- 性能优化:函数式组件本身已足够轻量,动态插槽不会带来明显性能问题,但避免过度复杂的插槽逻辑。
六、完整示例代码
父组件:
xml
<template>
<div>
<FunctionalDynamicSlots :active-slot="activeSlot">
<template slot="header">
<h2>固定头部</h2>
</template>
<template slot="content-1">
<p>动态内容 1</p>
</template>
<template slot="content-2">
<p>动态内容 2</p>
</template>
<template slot="footer">
<p>固定底部</p>
</template>
</FunctionalDynamicSlots>
<button @click="activeSlot = 'content-1'">显示内容1</button>
<button @click="activeSlot = 'content-2'">显示内容2</button>
</div>
</template>
<script>
import FunctionalDynamicSlots from './FunctionalDynamicSlots';
export default {
components: { FunctionalDynamicSlots },
data() {
return {
activeSlot: 'content-1'
};
}
};
</script>
函数式子组件:
arduino
// FunctionalDynamicSlots.js
export default {
functional: true,
props: {
activeSlot: {
type: String,
required: true
}
},
render(h, context) {
const { props, slots } = context;
const activeContent = slots()[props.activeSlot] || slots().default;
return h('div', { class: 'dynamic-slot-container' }, [
slots().header,
h('div', { class: 'active-content' }, activeContent),
slots().footer
]);
}
};
通过以上方案,你可以在 Vue 2 函数式组件中灵活实现动态插槽,满足复杂的内容分发需求。如果需要进一步扩展(如作用域插槽),可以结合 context.data.scopedSlots 处理,但动态插槽本身已覆盖大部分场景。