Vue 3 Composition API - 类似标签的组件中的“子”数据
Posted
技术标签:
【中文标题】Vue 3 Composition API - 类似标签的组件中的“子”数据【英文标题】:Vue 3 Composition API - 'child' data in a tabs-like component 【发布时间】:2021-10-24 19:10:59 【问题描述】:我有一个适用于 Vue 3 和 Vue 2 的 TabGroup/TabItem 组件,但它是在 Options API 中编写的。
我正在创建一个新的轮播组件,它共享许多功能,但我正在尝试在 Composition API 中编写它,因为这就是我们现在在项目中编写所有内容的方式。
我基本上是在尝试访问子组件上的数据,但我没有看到明显的方法。我希望能够确定组组件中哪些子组件处于活动状态,以便它可以管理键盘导航等操作。
我可以使用 Vuex,但这是一个通用组件,将在多个地方使用。我可以给每一个 ID 以在商店中区分它们,但这个组件最终也会进入一个库,它无法访问任何项目使用它的 Vuex 实例。 事件似乎不起作用,因为组件的设置方式 - 包装器不知道子级是什么,它们只是填充到默认槽中。似乎事件会在使用轮播的地方触发,而不是在组内。 出于同样的原因,我不能使用引用,它们将在创建轮播的地方可用,而不是在carousel-group
中。另外,我不希望任何人为了使用组件而必须向每个面板添加 ref。
我无法将面板作为数组提供给carousel-group
,因为每个面板都可以包含任何内容,我希望设置尽可能简单。
对于一些背景,基本设置是这样的:
<carousel-group>
<carousel-panel>
Panel content
</carousel-panel>
</carousel-group>
作为设置过程的一部分,我可以访问子面板,类似于我在 Options API 中所做的,并获取子面板的列表。
panels.value = slots.default().filter(child => child.type.name === 'carousel-panel');
每个carousel-panel
都有active
之类的实例数据,这是我要访问的。但是通过默认插槽获取面板并没有任何这些属性。
任何想法都会很棒,因为我现在有点难过。我真的不想使用 Options API 来执行此操作。
【问题讨论】:
$children
无论如何都是removed in Vue 3...
But those panels don't have any of the associated data being provided by the carousel-panel
的“数据”是指“插槽内容”,对吧?我不知道您是尝试访问嵌套实例属性还是仅访问插槽内容(“html”)
实例属性,将从setup
函数返回。比如每一个都有一个内部ID和一个active
属性。
【参考方案1】:
您正在尝试做的事情称为“复合组件”。 你需要使用 provide/inject(相当于 Vue 中的 React Context)并让 carousel-group 知道里面已经放了一个 carousel-panel。
我在表格组件中使用 registerChild 执行此操作。我从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>
显然这是一个表格组件,但您明白了 - 您可以通过在装载时注册它们来跟踪哪些面板位于轮播组内,感谢注入()使用父级的一些属性
另一件事是您要管理哪个轮播面板处于活动状态。哪个面板处于活动状态显然会保留在轮播组中,但您还需要使用 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>
然后让轮播组的内部数据属性从 registerChildren 的第一个窗格开始,然后在 5 秒后更改为第二个,依此类推。
https://github.com/3nuc/vue-compound-components/blob/master/example-dropdown/Example.vue
【讨论】:
我看到你也回复了我上面关于 instancec 属性的评论。您可以将所需的子实例属性放入const childInfo =
对象中,然后使用它们在父对象中注册子对象。在我上面的例子中,你肯定需要在 childInfo 中传递“id”属性,这样你就知道在 v-if 中显示哪个窗格)
有趣。我以前从未尝试过提供/注入,但这似乎是为这种情况而设计的。我相信提供/注入是反应性的,所以我可以摆脱在该组件内呈现窗格的内容吗?我试试看。
是的,注入/提供是一种方法。顺便说一句,您不需要在面板上使用id
。只需在面板中创建一个isActive
ref 并在注册时将其传递给一个组。家长现在可以通过设置其isActive.value
....来控制活动的孩子。以上是关于Vue 3 Composition API - 类似标签的组件中的“子”数据的主要内容,如果未能解决你的问题,请参考以下文章
为啥在 Vue 3 的 Composition API 中使用“API”这个词?