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

vue3.3更新了一个非常好用的东西,generic泛型组件,它能够推断出传入prop的类型,让我们得到更好的类型提示。看了一下,目前很少人讲得很好,于是开一篇文章讲一下,献丑了。

在vue3中,我们已经能够通过定义props的类型,得到比较好的组件类型提示。但是面对一种情况,我们似乎就无可奈何,那就是prop是复杂数据类型,严格来说,是不确定属性的对象。此时我们只能给一个object类型来定义,但导致的结果是,我们得不到很好的类型提示。

常见的例子是,拿ant-design-vue的a-table来说,它需要传入两个prop,dataSource和columns,其中dataSource的属性就是不确定的。ant-design-vue对dataSource的类型定义是object[],以至于使用作用域插槽时,得不到比较好的类型提示。

js 复制代码
<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"

ts 复制代码
// 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组件:

js 复制代码
<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文件

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,则重新得到了组件的方法

并且成功调用

相关推荐
牛奶6 小时前
Vue 基础理论 & API 使用
前端·vue.js·面试
牛奶7 小时前
Vue 底层原理 & 新特性
前端·vue.js·面试
pe7er7 小时前
状态提升:前端开发中的状态管理的设计思想
前端·vue.js·react.js
jonjia13 小时前
模块、脚本与声明文件
typescript
jonjia13 小时前
配置 TypeScript
typescript
jonjia13 小时前
TypeScript 工具函数开发
typescript
jonjia13 小时前
注解与断言
typescript
jonjia13 小时前
IDE 超能力
typescript
jonjia13 小时前
对象类型
typescript
jonjia13 小时前
快速搭建 TypeScript 开发环境
typescript