vue3.3更新了一个十分好用的东西,generic泛型组件,它可以推断出传入prop的类型,让咱们得到更好的类型提示。看了一下,现在很少人讲得很好,所以开一篇文章讲一下,献丑了。
在vue3中,咱们现已可以经过界说props的类型,得到比较好的组件类型提示。可是面对一种情况,咱们似乎就百般无奈,那便是prop是杂乱数据类型,严格来说,是不确定特点的目标。此时咱们只能给一个object类型来界说,但导致的结果是,咱们得不到很好的类型提示。
常见的例子是,拿ant-design-vue的a-table来说,它需求传入两个prop,dataSource和columns,其中dataSource的特点便是不确定的。ant-design-vue对dataSource的类型界说是object[],以至于运用作用域插槽时,得不到比较好的类型提示。
<template>
<a-table :dataSource="dataSource" :columns="columns">
<template #bodyCell="{ record, column }">
<span v-if="column.dataIndex === 'name'">{{ record }}</span>
</template>
</a-table>
</template>
<script setup lang="ts">
const dataSource = [
{
key: '1',
name: 'Mike',
age: 32,
address: '10 Downing Street',
},
{
key: '2',
name: 'John',
age: 42,
address: '10 Downing Street',
},
]
const columns = [
{
title: 'Name',
dataIndex: 'name',
key: 'name',
},
{
title: 'Age',
dataIndex: 'age',
key: 'age',
},
{
title: 'Address',
dataIndex: 'address',
key: 'address',
},
]
</script>
接着让咱们手写一个Table.vue,并用泛型组件来完善它,关键点在于运用generic="T extends object"
// Table.vue
<template>
<table>
<thead>
<tr>
<th v-for="(item, index) in columns" :key="index">{{ item.title }}</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in dataSource" :key="index">
<td v-for="header in columns">
<slot name="bodyCell" :record="item" :column="header">
{{ item[header.dataIndex as keyof T] }}
</slot>
</td>
</tr>
</tbody>
</table>
</template>
<script setup lang="ts" generic="T extends object">
defineProps<{
columns: { title: string; dataIndex: string; key: string }[]
dataSource: T[]
}>()
</script>
运用Table组件:
<template>
<a-table :dataSource="dataSource" :columns="columns" :pagination="false">
<template #bodyCell="{ record, column }">
<span v-if="column.dataIndex === 'name'">{{ record.name }}</span>
</template>
</a-table>
<Table :dataSource="dataSource" :columns="columns">
<template #bodyCell="{ record, column }">
<span v-if="column.dataIndex === 'name'">{{ record.name }}</span>
</template>
</Table>
</template>
<script setup lang="ts">
import Table from './components/Table.vue'
const dataSource = [
{
key: '1',
name: 'Mike',
age: 32,
address: '10 Downing Street',
},
{
key: '2',
name: 'John',
age: 42,
address: '10 Downing Street',
},
]
const columns = [
{
title: 'Name',
dataIndex: 'name',
key: 'name',
},
{
title: 'Age',
dataIndex: 'age',
key: 'age',
},
{
title: 'Address',
dataIndex: 'address',
key: 'address',
},
]
</script>
作用是如出一辙
不同的是,咱们得到了很好的类型提示:
存在问题:泛型组件ref实例丢掉
咱们在运用组件的时候,有时候也会用到组件实例上的一些办法,例如铲除form表单。运用泛型组件之后,会导致组件的实例丢掉,现在vue官方还未解决这个问题。
比如,在Table中露出一个铲除数据的办法
普通组件经过InstanceType<typeof Table>
就可以拿到组件的类型和办法,然后调用。可是运用了泛型组件,导致Table的类型丢掉,无法推导出类型来。
解决方案
幸好有大神找出了另一个办法,# vue3.3 generic 泛型组件无法获取实例类型解决方案
下面是演示:先创建一个ts文件
import Table from './components/Table.vue'
import { ref } from 'vue'
/**泛型组件出口类型 */
type GenericComponentExports<D extends (...p: any[]) => any> =
//这儿获取组件通用类型
import('vue').ComponentPublicInstance &
//这儿获取defineExpose露出的数据类型
Parameters<NonNullable<NonNullable<ReturnType<D>['__ctx']>['expose']>>[0]
export function useTableRef<T extends any[] = any[]>() {
//取得组件实例类型,可以正确的取得defineExpose露出的类型与通用的组件类型
type Instance = GenericComponentExports<typeof Table>
return ref<Instance & { data: T }>()
}
然后不再运用ref,改用这个useTableRef,则重新得到了组件的办法
并且成功调用