Vue3中向插槽传递渲染函数后,如何将其作为HTML元素使用?
Vue中插槽传递的渲染函数无法作为标签渲染的解决方法
问题描述
编写了如下TopBar组件:
<script setup lang="ts"> import { h } from 'vue'; const props = withDefaults(defineProps<{ height?: number }>(), { height: 50 }); const spacer = () => h('span', { class: 'bar__spacer' } ); </script> <template> <div class="bar" :style="{ '--height': props.height }"> <slot :spacer="spacer"></slot> </div> </template> <style scoped lang="scss"> .bar { position: relative; display: flex; flex: 0 0 calc(var(--height) * 1px); width: 100%; align-items: center; padding: 0 10px; background-color: var(--bg-top-bar); border-bottom: 1px solid var(--bg-separator); &__spacer { flex-grow: 1; } } </style>
使用时尝试通过插槽接收spacer并作为标签使用:
<TopBar v-slot="{ spacer }"> Some content goes here... <spacer/> Some more content there... </TopBar>
但<spacer/>被直接渲染为自定义标签,而非预期的带样式<span class="bar__spacer">元素。
原因分析
spacer是一个Vue渲染函数,不是Vue组件,无法直接以自定义标签的形式在模板中使用。模板里的<spacer/>会被解析为原生自定义元素,而非执行渲染函数生成的DOM。
解决方案
方案1:使用<component>标签调用渲染函数
Vue的<component>标签可以直接接收渲染函数作为:is的值,自动执行并渲染对应的内容:
<TopBar v-slot="{ spacer }"> Some content goes here... <component :is="spacer" /> Some more content there... </TopBar>
方案2:将spacer封装为组件而非渲染函数
修改TopBar组件,把spacer定义为Vue组件,这样就可以直接作为标签使用:
<script setup lang="ts"> import { h, defineComponent } from 'vue'; const props = withDefaults(defineProps<{ height?: number }>(), { height: 50 }); // 封装为组件 const Spacer = defineComponent({ render() { return h('span', { class: 'bar__spacer' }); } }); </script> <template> <div class="bar" :style="{ '--height': props.height }"> <slot :Spacer="Spacer"></slot> </div> </template> <!-- 样式部分保持不变 -->
使用时注意组件名要大写(Vue模板约定大写开头的标识符为组件):
<TopBar v-slot="{ Spacer }"> Some content goes here... <Spacer /> Some more content there... </TopBar>
补充说明
- 渲染函数本质是返回VNode的函数,必须被调用才能生成内容,
<component :is="spacer">会自动触发函数调用。 - Vue模板中,小写开头的标识符会被识别为原生元素,因此方案2中组件名需采用大写开头的命名规范。
内容的提问来源于stack exchange,提问作者Kindzoku
相关产品推荐
相关产品推荐

