【问题标题】:Accessing arbitrary slots in Vue访问 Vue 中的任意插槽
【发布时间】:2019-02-20 06:00:41
【问题描述】:

假设我想创建一个多层组件以供重复使用,例如 'Tab' Ui

所以使用开发者可以这样写:

<tabs>
  <tab label="My First Tab">
    Content for first tab which could contain components, html, etc.
  </tab>
  <tab label="My Second Tab">
    More Content
  </tab>
</tabs>

基本上,他们会以这样一种方式使用 Tabs 和 Tab 组件,以至于我不知道开发人员将在 Tabs 中使用多少个 Tab 组件。如何访问选项卡组件,例如,按选项卡 UI 功能显示和隐藏它们?

我尝试过使用this.$childrenthis.slots.default,但实际上无法访问选项卡数据来显示和隐藏它。公平地说,我正在用 Typescript 写作,因此问题可能会更加困难。

类似:

<template>
    <div class="tabs">
        <slot /> <!-- Location of the Tab's -->
    </div>
</template>

<script lang="ts">
import { Component, Vue } from "vue-property-decorator";

@Component({
    ...
})
export default class Tabs extends Vue {
    public tabs: any[] = [];
        
    public created() {
        this.tabs = this.$slots.default;
        // this.tabs is a set of VNode's, 
        //    I can't figure out how to access the components
        //    I see a prop 'componentInstance' but when I try to
        //    access it, it says undefined
        // this.select(0);
        let tab = this.tabs.find((obj: any, index: number) => {
            return index === 0;
        }); // tab is undefined :(
    }

    public select(index: number) {
        (this.$slots.default[index] as any).show = true;
        // Accessing Vnode instead of Component :(
        // this.$children[0]["show"] = true; // Read only :(
        // this.tabs[index].show = true; // blerrggg :(

        // Vue.nextTick();
    }
}
</script>

我在 Vue 中查看了一些用于 Tabs 的 GitHub 库,逻辑似乎非常复杂。我假设存在插槽/子组件,有一种更直接的方法来访问子组件。也许这对我来说过于乐观了。

如果您不知道子槽中有多少子槽(即您没有在父组件中明确编写它们),任何人都知道如何访问、传递或更改子槽中的数据?

【问题讨论】:

    标签: typescript vuejs2


    【解决方案1】:

    如果问题是你必须等到选项卡组件被挂载,mounted 生命周期钩子就可以解决这类事情。但是使用$children 属性存在潜在问题,如Vue docs 中所述:

    请注意,$children 没有订单保证,而且它不是反应式的。如果您发现自己尝试使用 $children 进行数据绑定,请考虑使用 Array 和 v-for 生成子组件,并使用 Array 作为事实来源。

    由于不保证订单,this.$children[index] 不保证给你你期望的标签。

    如果您采用建议的方法,使用v-for 和一个数组,它可能看起来像这样:

    <template>
        <div class="tabs">
            <tab v-for="(tabName, index) in tabs" :key="tabName" :label="tabName" :ref="`tab-${index}`">
                <slot :name="tabName"></slot>
            </tab>
        </div>
    </template>
    
    <script lang="ts">
    import { Component, Prop, Vue } from "vue-property-decorator";
    
    @Component({
        ...
    })
    export default class Tabs extends Vue {
        @Prop(Array) tabs: string[];
    
        public mounted() {
            this.select(0);
        }
    
        public select(index: number) {
            this.$refs[`tab-${index}`].show = true;
        }
    }
    </script>
    

    我将插槽放在选项卡中,以便您知道所有子组件都将被包装在选项卡组件中,这意味着所有tabs 子组件都保证是tab 组件,而您的$refs 将都有预期的tab 属性。你可以这样使用:

    <tabs :tabs="['My First Tab', 'My Second Tab']">
        <template slot="My First Tab">
            Content for first tab which could contain components, html, etc.
        </template>
        <template slot="My Second Tab">
            More content
        </template>
    </tabs>
    

    【讨论】:

    • 欢迎来到stackoverflow。这是一个很棒的答案。
    【解决方案2】:

    我的部分问题(似乎)是标签的created() 函数在标签组件安装之前触发,因此它们可能存在也可能不存在。通过使用超时调用初始选择,我能够使用

    (this.$children[index] as any)["show"] = true;
    

    有谁知道如何检查何时(立即)安装子组件?我做了一些搜索,但找不到正确的答案。

    【讨论】:

      【解决方案3】:

      我能够通过使用updated life-cycle hook 后跟this.$nextTick() 来解决它。总之,它们允许您在每次组件更新时触发某些东西(对我来说,这是在等待关于子元素的 axios 帖子)。

      总结起来就是这样:

      mounted() {
        // this makes reference to the children inside the (current) component slots
        const tabs: Tab[] = this.$vnode.componentInstance.$children as Tab[];
        setTabs(tabs);
      }
      
      updated() {
        this.$nextTick(function () {
          const updatedTabs: Tab[] = this.$vnode.componentInstance.$children as Tab[];
      
          if (updatedTabs.length > this.tabs.length) setTabs(updatedTabs);
        });
      }
      
      setTabs(updatedTabs: Tab[]) {
        this.tabs = updatedTabs;
        // Do some more stuff here if you want (like filtering or ordering)
      }
      

      【讨论】:

        猜你喜欢
        • 2021-02-01
        • 1970-01-01
        • 2021-09-12
        • 2020-05-27
        • 2019-06-08
        • 2021-02-06
        • 1970-01-01
        • 2013-10-11
        • 1970-01-01
        相关资源
        最近更新 更多