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

并且成功调用

相关推荐
LIUENG1 小时前
Vue3 响应式原理
前端·vue.js
wycode2 小时前
Vue2实践(3)之用component做一个动态表单(二)
前端·javascript·vue.js
wycode3 小时前
Vue2实践(2)之用component做一个动态表单(一)
前端·javascript·vue.js
第七种黄昏3 小时前
Vue3 中的 ref、模板引用和 defineExpose 详解
前端·javascript·vue.js
pepedd8644 小时前
还在开发vue2老项目吗?本文带你梳理vue版本区别
前端·vue.js·trae
前端缘梦4 小时前
深入理解 Vue 中的虚拟 DOM:原理与实战价值
前端·vue.js·面试
Data_Adventure4 小时前
Java 与 TypeScript 的“同名方法”之争:重载机制大起底
前端·typescript
HWL56794 小时前
pnpm(Performant npm)的安装
前端·vue.js·npm·node.js
柯南95275 小时前
Vue 3 reactive.ts 源码理解
vue.js
柯南95275 小时前
Vue 3 Ref 源码解析
vue.js