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

并且成功调用

相关推荐
什么什么什么?43 分钟前
el-input实现金额输入
javascript·vue.js·elementui
工业互联网专业2 小时前
基于springboot+vue的融合多源高校画像数据与协同过滤算法的高考择校推荐系统
java·vue.js·spring boot·毕业设计·源码·课程设计·高考择校推荐系统
狼性书生2 小时前
uniapp vue3实现的一款数字动画调节器件,支持长按、单点操作,提供丝滑的增减动画效果
前端·vue.js·微信小程序·小程序·uni-app
qq_12498707532 小时前
Java+Vue+uniapp微信小程序校园自助打印系统(程序+论文+讲解+安装+调试+售后)
vue.js·微信小程序·uni-app·毕业设计
热爱蛋炒饭3 小时前
ruoyi vue el-elementui el-tree 自适应宽度向左浮动
javascript·vue.js·elementui
编程诗人华仔4 小时前
若依框架实际国际化前后端统一解决方案
java·vue.js·spring boot·后端·elementui
bin91534 小时前
DeepSeek 助力 Vue3 开发:打造丝滑的列表(List)
前端·javascript·vue.js·ecmascript·deepseek
微软MVP Eleven5 小时前
【朝夕教育】《鸿蒙原生应用开发从零基础到多实战》003-TypeScript 中的类
华为·typescript·harmonyos
i建模6 小时前
React + TypeScript 数据血缘分析实战
前端·数据库·react.js·typescript·数据管理·数据血缘
化作繁星6 小时前
在 Vue 3 中,如何缓存和复用动态组件
前端·vue.js·缓存