在使用 <slot/> 创建 re-usable 和动态选项卡菜单的组件内部使用 v-for

Using v-for inside component that uses <slot/> to create re-usable and dynamic Tab menu

所以我正在尝试使用 Vue 3 和插槽创建一个动态选项卡菜单。我让选项卡工作,我有 BaseTabsWrapper 和 BaseTab 组件。我需要能够 v-for 在 BaseTabsWrapper 组件中使用 BaseTab 组件。像这样:

            <section
                id="content"
                class="w-full mx-2 pr-2"
                v-if="incomingChatSessions && incomingChatSessions.length"
            >
                <BaseTabsWrapper>
                    <BaseTab
                        v-for="chatSession in incomingChatSessions"
                        :key="chatSession.id"
                        :title="chatSession.endUser.name"
                    >
                        <p>{{ chatSession }}</p>
                    </BaseTab>
                </BaseTabsWrapper>
            </section>

我发现的答案中的一个重要警告是 incomingChatSessions object 是异步的并且来自 websocket(我已经测试过这个 object 工作正常并带来所有数据正确地又名永远不会是空的 object).

BaseTabsWrapper 模板内部。重要部分:

<template>
    <div>
        <ul
            class="tag-menu flex space-x-2"
            :class="defaultTagMenu ? 'default' : 'historic'"
            role="tablist"
            aria-label="Tabs Menu"
            v-if="tabTitles && tabTitles.length"
        >
            <li
                @click.stop.prevent="selectedTitle = title"
                v-for="title in tabTitles"
                :key="title"
                :title="title"
                role="presentation"
                :class="{ selected: title === selectedTitle }"
            >
                <a href="#" role="tab">
                    {{ title }}
                </a>
            </li>
        </ul>
        <slot />
    </div>
</template>

和脚本:

<script>
import { ref, useSlots, provide } from 'vue'
export default {
    props: {
        defaultTagMenu: {
            type: Boolean,
            default: true,
        },
    },
    setup(props) {
        const slots = useSlots()
        const tabTitles = ref(
            slots.default()[0].children.map((tab) => tab.props.title)
        )
        const selectedTitle = ref(tabTitles.value[0])
        provide('selectedTitle', selectedTitle)
        provide('tabTitles', tabTitles)
        return {
            tabTitles,
            selectedTitle,
        }
    },
}
</script>

这是Tab组件模板:

<template>
    <div v-show="title === selectedTitle" class="mt-4">
        <slot />
    </div>
</template>

<script>
import { inject } from 'vue'
export default {
    props: {
        title: {
            type: String,
            default: 'Tab Title',
        },
    },
    setup() {
        const selectedTitle = inject('selectedTitle')
        return {
            selectedTitle,
        }
    },
}
</script>

我脚本中最重要的部分也是给我带来很多麻烦的部分是:

    const tabTitles = ref(
        slots.default()[0].children.map((tab) => tab.props.title)
    )

我在这里做的是根据每个插槽的 属性“标题”创建一组标签标题,但是当我加载页面时,这个数组总是只有一个标题,即使我' m 从 API 获取更多标题元素。我注意到的一件事是,如果我从我的代码中强制使用页面的 re-render,那么 tabTitles 数组具有正确数量的元素,并且我在菜单上获得了所有正确数量的选项卡。我已经测试过,我使用来自 websocket 的数据控制异步性以隐藏“incomingChatSessions”数组的方式一切正常,但无论我如何尝试,tabTiles 总是只获取一个元素。

我会做类似的事情:

computed(   
    () => slots.default()[0].children.map((tab) => tab.props.title) 
) 

它应该在更新组件时更新计算的 属性(例如插槽更改)