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>

vue3.3更新了generic泛型组件,从速学起来,更好的封装你的组件把

vue3.3更新了generic泛型组件,从速学起来,更好的封装你的组件把

接着让咱们手写一个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>

作用是如出一辙

vue3.3更新了generic泛型组件,从速学起来,更好的封装你的组件把

不同的是,咱们得到了很好的类型提示:

vue3.3更新了generic泛型组件,从速学起来,更好的封装你的组件把

vue3.3更新了generic泛型组件,从速学起来,更好的封装你的组件把

存在问题:泛型组件ref实例丢掉

咱们在运用组件的时候,有时候也会用到组件实例上的一些办法,例如铲除form表单。运用泛型组件之后,会导致组件的实例丢掉,现在vue官方还未解决这个问题。

比如,在Table中露出一个铲除数据的办法

vue3.3更新了generic泛型组件,从速学起来,更好的封装你的组件把

vue3.3更新了generic泛型组件,从速学起来,更好的封装你的组件把
普通组件经过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,则重新得到了组件的办法

vue3.3更新了generic泛型组件,从速学起来,更好的封装你的组件把
并且成功调用

vue3.3更新了generic泛型组件,从速学起来,更好的封装你的组件把