Vue 3 Composition API - 类似选项卡的组件中的“子”数据

2024-03-29

我有一个适用于 Vue 3 和 Vue 2 的 TabGroup/TabItem 组件,但它是在选项 API 中编写的。

我正在创建一个新的轮播组件,它共享许多功能,但我尝试将其编写在 Composition API 中,因为这就是我们现在在项目中编写所有内容的方式。

我本质上是在尝试访问子组件上的数据,但我没有看到明显的方法。我希望能够确定哪个子组件在组组件中处于活动状态,因此它可以管理键盘导航等内容。

  • 我可以使用 Vuex,但这是一个通用组件,将在多个地方使用。我可以为每个组件提供某种 ID 来区分它们在商店中的位置,但该组件最终也会进入一个库,该库无法访问正在使用它的任何项目的 Vuex 实例。
  • 由于组件的设置方式,事件似乎不起作用 - 包装器不知道子组件是什么,它们只是填充到默认插槽中。事件似乎会在使用轮播的地方触发,而不是在组内触发。
  • 出于同样的原因,我不能使用 refs,它们将在创建轮播的地方可用,而不是在carousel-group。另外,我不希望任何人为了使用该组件而必须向每个面板添加引用。
  • 我无法将面板作为数组提供给carousel-group,因为每个面板都可以包含任何内容,我希望它尽可能容易设置。

对于一些背景,基本设置是这样的:

<carousel-group>
    <carousel-panel>
        Panel content
    </carousel-panel>
</carousel-group>

作为设置过程的一部分,我可以访问子面板,类似于我在选项 API 中所做的操作,并获取子面板的列表。

panels.value = slots.default().filter(child => child.type.name === 'carousel-panel');

Each carousel-panel具有实例数据,例如active,这就是我想要访问的。但是通过默认插槽获取面板并不具有任何这些属性。

任何想法都会很棒,因为我现在有点困惑。我真的不想使用选项 API 来执行此操作。


您想要做的事情称为“复合组件”。 您需要使用provide/inject(相当于Vue中的ReactContext)并让carousel-group知道carousel-panel已放入其中。

我使用表组件中的 registerChild 来执行此操作。我从我的仓库中获取了这个https://github.com/sethidden/vue-compound-components/tree/master/example-table https://github.com/sethidden/vue-compound-components/tree/master/example-table

<!-- MyTable.vue -->
<script setup>
import {ref, provide, defineProps } from 'vue'

const props = defineProps({
  items: {
    type: Array,
    required: true,
  }
})

const registerChild = (child) => registeredChildren.value.push(child);
const unregisterChild = (child) => {
  registeredChildren.value = 
    registeredChildren.value.filter(
      registeredChild => registeredChild !== child
    );
};
  
const registeredChildren = ref([]);

provide('TheParent', { registerChild, unregisterChild })
  
</script>
<template>
columns: {{ registeredChildren.map(x=>x.itemPropertyName) }}
<table>
  <thead>
    <tr>
      <th v-for="column in registeredChildren" :key="column">
        <component :is="column.content" ref="column"/>
        {{ column.sortable ? 'sortable' : ''}}
      </th>
    </tr>
  </thead>
  <tbody>
    <tr v-for="item, i in items" :key="i">
      <td v-for="column, j in registeredChildren" :key="j">
        {{ item[column.itemPropertyName] }}
      </td>
    </tr>
  </tbody>
</table>

<!-- we want the mounted() lifecycle to fire on the components that are in the slot, but we dont want to show them -->
<div v-show="false">
  <slot/>
</div>
</template>

<!-- MyTableColumn.vue -->
<script setup>
import {onMounted,onBeforeUnmount,inject, defineProps, h, render, useContext} from 'vue'

const props = defineProps({
  itemPropertyName: {
    type: String,
    required: true,
  },
  sortable: {
    type: Boolean,
    default: false
  }
})
const { registerChild, unregisterChild } = inject('TheParent');
const ctx = useContext();

const childInfo = {
  itemPropertyName: props.itemPropertyName,
  sortable: props.sortable,

  // since ctx.slots.default is a render fn,
  // we pass slot content from here to the parent component
  // then render it in the parent using <component :is=""/>
  content: ctx.slots.default
}

registerChild(childInfo);
onBeforeUnmount(() => {
  unregisterChild(childInfo);
})

</script>
<template>
<div>
  <slot/>
</div>
</template>

显然,这是一个表格组件,但您明白了 - 您可以通过在安装上注册哪些面板来跟踪哪些面板位于轮播组内,并使用来自父级的某些属性,这要归功于inject()

另一件事是您想要管理哪个轮播面板处于活动状态。哪个面板处于活动状态显然会保留在轮播组中,但您还需要使用以下命令切换面板的可见性v-for="panes in registeredChildren" v-if="activePane === pane.id",但这可以通过以下方式实现:

<carousel-group>
  <carousel-pane id="1"/>
  <carousel-pane id="2"/>
  <carousel-pane id="3"/>
</carousel-group>

然后让轮播组的内部数据属性从来自 RegisteredChildren 的第一个窗格开始,然后在 5 秒后更改为第二个窗格,依此类推。

https://github.com/3nuc/vue-compound-components/blob/master/example-dropdown/Example.vue https://github.com/3nuc/vue-compound-components/blob/master/example-dropdown/Example.vue

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Vue 3 Composition API - 类似选项卡的组件中的“子”数据 的相关文章

随机推荐