Vue3开发Tabs组件时slot内Tab组件Teleport内容不显示如何解决?
问题原因及解决方案
核心原因
Teleport内容不渲染是以下几个错误共同导致的:
- 父组件容器ID绑定错误:你在
tabs.vue里写的:id="tabs-top-tabId"是硬编码字符串,并没有把tabId变量的值拼接到ID中,实际生成的容器ID固定是tabs-top-tabId,不是你预期的带随机值的ID - 子组件Teleport目标选择器和父容器ID不匹配:即使修复父组件的ID绑定,子组件里的
:to="#tabs-top-tabId"也是写死的,拿不到父组件动态生成的tabId值,找不到对应的目标容器 - 事件绑定位置错误:你把
@click事件绑在了<teleport>组件本身,这个标签不会生成真实DOM,事件不会触发
具体修复步骤
1. 修复父组件ID绑定 + 传递上下文给子组件
修改tabs.vue的模板和setup逻辑,用provide把动态生成的tabId、选中状态、切换方法传递给所有子tab组件:
<template> <section :class="mode ? 'tabs--light' : 'tabs--dark'"> <!-- 修复ID拼接,把tabId变量值拼进去 --> <div :id="`tabs-top-${tabId}`" class="tabs__menu"></div> <slot></slot> </section> </template> <script lang="ts"> import { defineComponent, reactive, toRefs, provide } from 'vue' export default defineComponent({ setup () { const tabId = Math.random().toString(36).slice(2) // 临时生成随机字符串ID const data = reactive<{selectedIndex: number}>({ selectedIndex: 0 }) const selectTab = (i: number) => { data.selectedIndex = i } // 把tab相关上下文提供给子组件 provide('tabsContext', { tabId, selectedIndex: data.selectedIndex, selectTab }) return { tabId, ...toRefs(data), selectTab } } }) </script>
2. 修复子组件Teleport逻辑
修改tab.vue,注入父组件提供的上下文,动态拼接Teleport的目标选择器,把事件移到内部真实DOM元素上:
<template> <div class="tab" v-bind="$attrs"> <!-- 动态拼接目标选择器,和父容器ID对应 --> <teleport :to="`#tabs-top-${tabsContext.tabId}`"> <!-- 点击事件绑到真实DOM元素上 --> <span style="color: red" @click="tabsContext.selectTab(index)"> {{ title }} </span> </teleport> <keep-alive> <slot v-if="isActive"></slot> </keep-alive> </div> </template> <script lang="ts"> import { defineComponent, PropType, inject } from 'vue' export default defineComponent({ props: { title: { type: String as PropType<string>, required: true }, isActive: { type: Boolean as PropType<boolean>, default: false }, index: { type: Number as PropType<number>, required: true } }, setup() { // 注入父组件提供的上下文 const tabsContext = inject('tabsContext') return { tabsContext } } }) </script>
3. 调整组件使用方式
使用时给每个b-tab传入对应的索引值即可:
<b-tabs> <b-tab :title="'tab 1'" :is-active="false" :index="0" > tab content1 </b-tab> <b-tab :title="'tab 2'" :is-active="false" :index="1" > tab content2 </b-tab> <b-tab :title="'tab 3'" :is-active="true" :index="2" > tab content3 </b-tab> </b-tabs>
补充说明
如果不想手动传index,你可以在父组件遍历slot子节点的时候自动给每个tab注入index值,能进一步简化使用体验。
内容的提问来源于stack exchange,提问作者BT101
相关产品推荐
相关产品推荐

