Vue中通过函数式包装器传递插槽时如何保留子组件原有插槽?
问题描述
现有如下代码:
父组件代码
<script setup> import { ref } from 'vue' import Input from './Input.js' import Text from './Text.vue' const msg = ref('Hello World!') </script> <template> <Input label="Message"> <Text v-model="msg"> <template #icon>❤️</template> </Text> </Input> </template>
Input.js
import {h} from 'vue'; export default function Input({label}, {slots}){ return slots.default().map(vnode => h(vnode, {}, {label: () => label})) };
Text.vue
<script setup> const model = defineModel(); </script> <template> <div :class="{iconed: $slots.icon}"> <div v-if="$slots.label"><slot name="label"/></div> <span v-if="$slots.icon" class="icon"><slot name="icon"/></span> <input v-model="model"/> </div> </template>
需求是保留Text组件的icon插槽,且希望保持现有模板写法,明确该插槽属于Text组件(已知可将插槽移至Input组件再转发,但不希望这么做)。
解决方案
问题根源在于Input组件创建新vnode时,直接用{label: () => label}替换了原vnode的插槽集合,导致Text组件的icon插槽丢失。只需合并原插槽与新增的label插槽即可解决:
修改Input.js代码如下:
import {h} from 'vue'; export default function Input({label}, {slots}){ return slots.default().map(vnode => { // 合并原vnode的所有插槽与新增的label插槽 const mergedSlots = { ...vnode.children, label: () => label } return h(vnode, {}, mergedSlots) }) };
此修改既保留了Text组件的icon插槽,又能正常传递Input的label插槽,完全无需改动原模板结构,仍可明确icon插槽属于Text组件。
内容的提问来源于stack exchange,提问作者Alexander Nenashev
相关产品推荐
相关产品推荐

