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

并且成功调用

相关推荐
我只会写Bug啊6 小时前
Vue文件预览终极方案:PNG/EXCEL/PDF/DOCX/OFD等10+格式一键渲染,开源即用!
前端·vue.js·pdf·excel·预览
一 乐9 小时前
高校后勤报修系统|物业管理|基于SprinBoot+vue的高校后勤报修系统(源码+数据库+文档)
java·前端·javascript·数据库·vue.js·毕设
那年窗外下的雪.10 小时前
鸿蒙ArkUI布局与样式进阶(十五)—— 模块化 · 自定义组件 · 泛型机制深度解析
javascript·华为·typescript·harmonyos·鸿蒙·arkui
薛一半11 小时前
Vue3的Pinia详解
前端·javascript·vue.js
guangzan13 小时前
React 状态管理的“碎片化”
typescript·zustand
前端付豪14 小时前
如何使用 Vuex 设计你的数据流
前端·javascript·vue.js
咖啡の猫15 小时前
Vue消息订阅与发布
前端·javascript·vue.js
爱看书的小沐16 小时前
【小沐杂货铺】基于Three.js绘制三维管道Pipe(WebGL、vue、react)
javascript·vue.js·webgl·three.js·管道·pipe·三维管道
前端开发爱好者18 小时前
Vue 团队成员又搞了个 "新玩具"!
前端·javascript·vue.js
一 乐18 小时前
农产品销售系统|农产品电商|基于SprinBoot+vue的农产品销售系统(源码+数据库+文档)
java·javascript·数据库·vue.js·spring boot·后端·农产品销售系统