【问题标题】:Vue 3 how to get information about $childrenVue 3 如何获取有关 $children 的信息
【发布时间】:2021-01-17 02:16:58
【问题描述】:

这是我在标签组件中使用 VUE 2 的旧代码:

created() {
   this.tabs = this.$children;
}

标签:

<Tabs> 
  <Tab title="tab title">
    ....
  </Tab>
  <Tab title="tab title">
    ....
  </Tab> 
</Tabs>

VUE 3: 如何使用组合 API 在 Tabs 组件中获取有关子项的一些信息?获取长度,迭代它们,并创建选项卡标题,......等等?有任何想法吗? (使用组合 API)

【问题讨论】:

    标签: vue.js vuejs3 vue-composition-api


    【解决方案1】:

    根据Vue documentation,假设您在Tabs 组件下有一个默认插槽,您可以直接在模板中访问该插槽的子项,如下所示:

    // Tabs component
    
    <template>
      <div v-if="$slots && $slots.default && $slots.default()[0]" class="tabs-container">
        <button
          v-for="(tab, index) in getTabs($slots.default()[0].children)"
          :key="index"
          :class="{ active: modelValue === index }"
          @click="$emit('update:model-value', index)"
        >
          <span>
            {{ tab.props.title }}
          </span>
        </button>
      </div>
      <slot></slot>
    </template>
    
    <script setup>
      defineProps({ modelValue: Number })
    
      defineEmits(['update:model-value'])
    
      const getTabs = tabs => {
        if (Array.isArray(tabs)) {
          return tabs.filter(tab => tab.type.name === 'Tab')
        } else {
          return []
        }
    </script>
    
    <style>
    ...
    </style>
    

    Tab 组件可能类似于:

    // Tab component
    
    <template>
      <div v-show="active">
        <slot></slot>
      </div>
    </template>
    
    <script>
      export default { name: 'Tab' }
    </script>
    
    <script setup>
      defineProps({
        active: Boolean,
        title: String
      })
    </script>
    

    实现应该类似于以下内容(考虑一个对象数组,每个部分一个,带有titlecomponent):

    ...
    <tabs v-model="active">
      <tab
        v-for="(section, index) in sections"
        :key="index"
        :title="section.title"
        :active="index === active"
      >
        <component
          :is="section.component"
        ></component>
      </app-tab>
    </app-tabs>
    ...
    <script setup>
    import { ref } from 'vue'
    
    const active = ref(0)
    </script>
    

    另一种方法是使用 useSlots,如 Vue 文档中所述(上面的链接)。

    【讨论】:

      【解决方案2】:

      哦,伙计们,我解决了:

      this.$slots.default().filter(child => child.type.name === 'Tab')
      

      【讨论】:

      • 你从哪里得到那个“插槽”?
      • 改用this.$slots.default()
      • @Katinka -> 在组合 API 设置方法中 => setup(_, {slots})
      • 不确定他们为什么不推荐使用 $children 属性 :( 此方法有效,但需要像在模板中一样将任何数据传递给插槽 this.$slots.default({....})
      【解决方案3】:

      我遇到了同样的问题,经过大量研究并问自己为什么他们删除了$children,我发现他们创造了一个更好、更优雅的替代方案。

      这是关于动态组件的。 (&lt;component: is =" currentTabComponent "&gt; &lt;/component&gt;)。

      我在这里找到的信息:

      https://v3.vuejs.org/guide/component-basics.html#dynamic-components

      希望对大家有用,大家好!!

      【讨论】:

      • 动态组件早在几年前就已经在 Vue 2 中可用了。不确定它们与访问子组件有什么关系
      【解决方案4】:

      在 3.x 中,$children 属性已被移除且不再受支持。相反,如果您需要访问子组件实例,他们建议使用 $refs。作为一个数组

      https://v3.vuejs.org/guide/migration/children.html#_2-x-syntax

      【讨论】:

        【解决方案5】:

        我对 Ingrid Oberbüchler 的组件进行了小幅改进,因为它不支持热重载/动态选项卡。

        在 Tab.vue 中:

        onBeforeMount(() => {
          // ...
        })
        onBeforeUnmount(() => {
          tabs.count--
        })
        

        在 Tabs.vue 中:

        const selectTab = // ...
        // ...
        watch(
          () => state.count,
          () => {
            if (slots.default) {
              state.tabs = slots.default().filter((child) => child.type.name === "Tab")
            }
          }
        )
        

        【讨论】:

          【解决方案6】:

          这是我现在的 Vue 3 组件。我使用提供来获取子Tab 组件中的信息。

          <template>
            <div class="tabs">
              <div class="tabs-header">
                <div
                  v-for="(tab, index) in tabs"
                  :key="index"
                  @click="selectTab(index)"
                  :class="{'tab-selected': index === selectedIndex}"
                  class="tab"
                >
                  {{ tab.props.title }}
                </div>
              </div>
              <slot></slot>
            </div>
          </template>
          
          <script lang="ts">
          import {defineComponent, reactive, provide, onMounted, onBeforeMount, toRefs, VNode} from "vue";
          
          interface TabProps {
            title: string;
          }
          
          export default defineComponent({
            name: "Tabs",
            setup(_, {slots}) {
              const state = reactive({
                selectedIndex: 0,
                tabs: [] as VNode<TabProps>[],
                count: 0
              });
          
              provide("TabsProvider", state);
          
              const selectTab = (i: number) => {
                state.selectedIndex = i;
              };
          
              onBeforeMount(() => {
                if (slots.default) {
                  state.tabs = slots.default().filter((child) => child.type.name === "Tab");
                }
              });
          
              onMounted(() => {
                selectTab(0);
              });
          
              return {...toRefs(state), selectTab};
            }
          });
          </script>
          

          标签组件:

          export default defineComponent({
            name: "Tab",
            setup() {
              const index = ref(0);
              const isActive = ref(false);
          
              const tabs = inject("TabsProvider");
          
              watch(
                () => tabs.selectedIndex,
                () => {
                  isActive.value = index.value === tabs.selectedIndex;
                }
              );
          
              onBeforeMount(() => {
                index.value = tabs.count;
                tabs.count++;
                isActive.value = index.value === tabs.selectedIndex;
              });
              return {index, isActive};
            }
          });
          
          
          <div class="tab" v-show="isActive">
              <slot></slot>
          </div>
          

          【讨论】:

          • 目前还不清楚,如何使标签处于活动状态。使用 $children 我有组件实例并且可以编写“tab.active = true”。但现在选项卡是 VNode。您存储 selectedIndex,但如何在子选项卡中使用它?
          【解决方案7】:

          致想要完整代码的人:

          标签.vue

          <template>
              <div>
                  <div class="tabs">
                      <ul>
                          <li v-for="tab in tabs" :class="{ 'is-active': tab.isActive }">
                              <a :href="tab.href" @click="selectTab(tab)">{{ tab.name }}</a>
                          </li>
                      </ul>
                  </div>
          
                  <div class="tabs-details">
                      <slot></slot>
                  </div>
              </div>
          </template>
          
          <script>
              export default {
                  name: "Tabs",
                  data() {
                      return {tabs: [] };
                  },
                  created() {
          
                  },
                  methods: {
                      selectTab(selectedTab) {
                          this.tabs.forEach(tab => {
                              tab.isActive = (tab.name == selectedTab.name);
                          });
                      }
                  }
              }
          </script>
          
          <style scoped>
          
          </style>
          

          Tab.vue

          <template>
              <div v-show="isActive"><slot></slot></div>
          </template>
          
          <script>
              export default {
                  name: "Tab",
                  props: {
                      name: { required: true },
                      selected: { default: false}
                  },
          
                  data() {
          
                      return {
                          isActive: false
                      };
          
                  },
          
                  computed: {
          
                      href() {
                          return '#' + this.name.toLowerCase().replace(/ /g, '-');
                      }
                  },
          
                  mounted() {
          
                      this.isActive = this.selected;
          
                  },
          
                  created() {
          
                      this.$parent.tabs.push(this);
          
                  },
              }
          </script>
          
          <style scoped>
          
          </style>
          

          App.js

          <template>
              <Tabs>
                              <Tab :selected="true"
                                   :name="'a'">
                                  aa
                              </Tab>
                              <Tab :name="'b'">
                                  bb
                              </Tab>
                              <Tab :name="'c'">
                                  cc
                              </Tab>
                          </Tabs>
          <template/>
          

          【讨论】:

          • 谢谢,但这不是 Vue 3 + 组合 API 的解决方案。我在设置方法'setup(_,{slots})'中解决它
          【解决方案8】:

          如果你复制粘贴的代码跟我一样

          然后只需将创建的方法添加到“选项卡”组件中,该方法会将自身添加到其父项的选项卡数组中

          created() {
              
                  this.$parent.tabs.push(this); 
          
              },
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2019-07-07
            • 2022-10-25
            • 2011-07-22
            • 2021-03-01
            • 2020-04-24
            • 2014-11-21
            相关资源
            最近更新 更多