TS和組件綁定過(guò)程(泛型表格)
一、React + TS 泛型通用表格
GenericTable.tsx
import React from 'react';
// 列配置類型
export type TableColumn<T> = {
key: keyof T;
title: string;
width?: number;
render?: (val: T[keyof T], record: T) => React.ReactNode;
};
// 組件入?yún)?
interface GenericTableProps<T> {
columns: TableColumn<T>[];
dataSource: T[];
loading?: boolean;
}
// 泛型組件寫法
function GenericTable<T>(props: GenericTableProps<T>) {
const { columns, dataSource, loading = false } = props;
if (loading) return <div>加載中...</div>;
return (
<table border={1} cellPadding={6} cellSpacing={0}>
<thead>
<tr>
{columns.map((col) => (
<th key={String(col.key)} style={{ width: col.width }}>
{col.title}
</th>
))}
</tr>
</thead>
<tbody>
{dataSource.map((record, idx) => (
<tr key={idx}>
{columns.map((col) => {
const val = record[col.key];
return (
<td key={String(col.key)}>
{col.render ? col.render(val, record) : String(val)}
</td>
);
})}
</tr>
))}
</tbody>
</table>
);
}
export default GenericTable;使用示例
import GenericTable from './GenericTable';
// 業(yè)務(wù)實(shí)體
interface UserItem {
id: number;
name: string;
status: 0 | 1;
}
function Demo() {
const columns: TableColumn<UserItem>[] = [
{ key: 'id', title: 'ID' },
{ key: 'name', title: '姓名' },
{
key: 'status',
title: '狀態(tài)',
render: (val) => (val === 1 ? '啟用' : '禁用')
}
];
const data: UserItem[] = [
{ id: 1, name: '張三', status: 1 },
{ id: 2, name: '李四', status: 0 }
];
return <GenericTable columns={columns} dataSource={data} />;
}二、Vue3 + TS 標(biāo)準(zhǔn)公共組件模板
BaseDialog.vue
<template>
<!-- 遮罩層 -->
<div
class="base-dialog-mask"
v-if="visible"
@click.self="handleCloseMask"
>
<!-- 彈窗容器 -->
<div class="base-dialog" :style="dialogStyle">
<!-- 頭部 -->
<div class="dialog-header">
<slot name="header">
<span class="title">{{ title }}</span>
</slot>
<span class="close-btn" @click="handleClose">×</span>
</div>
<!-- 默認(rèn)插槽:主體內(nèi)容 -->
<div class="dialog-body">
<slot />
</div>
<!-- 底部 -->
<div class="dialog-footer" v-if="showFooter">
<slot name="footer">
<button class="btn cancel-btn" @click="handleClose">取消</button>
<button class="btn confirm-btn" @click="handleConfirm">確定</button>
</slot>
</div>
</div>
</div>
</template>
<script setup lang="ts">
// ?? 1. 先定義類型:?jiǎn)为?dú)抽離,不寫在行內(nèi)
type DialogSize = 'small' | 'middle' | 'large';
interface BaseDialogProps {
// 必傳
visible: boolean
// 可選 + 默認(rèn)值
title?: string
size?: DialogSize
width?: string
showFooter?: boolean
closeOnMask?: boolean
}
// ?? 2. props 定義 + 精準(zhǔn)默認(rèn)值(TS 標(biāo)準(zhǔn)寫法)
const props = withDefaults(defineProps<BaseDialogProps>(), {
title: '提示',
size: 'middle',
showFooter: true,
closeOnMask: true,
width: ''
})
// ?? 3. 嚴(yán)格定義 emits 事件類型
interface DialogEmits {
(e: 'update:visible', val: boolean): void
(e: 'confirm'): void
(e: 'close'): void
}
const emit = defineEmits<DialogEmits>()
// ?? 4. 計(jì)算彈窗寬度(根據(jù) size 適配)
const dialogStyle = computed(() => {
const sizeMap: Record<DialogSize, string> = {
small: '400px',
middle: '600px',
large: '800px'
}
return {
width: props.width || sizeMap[props.size]
}
})
// ?? 5. 內(nèi)部事件方法
const handleClose = () => {
emit('update:visible', false)
emit('close')
}
const handleConfirm = () => {
emit('confirm')
}
const handleCloseMask = () => {
if (props.closeOnMask) {
handleClose()
}
}
// ?? 6. 對(duì)外暴露組件實(shí)例方法(父組件可 ref 調(diào)用)
defineExpose({
handleClose,
handleConfirm
})
</script>
<style scoped>
.base-dialog-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 999;
}
.base-dialog {
background: #fff;
border-radius: 8px;
overflow: hidden;
}
.dialog-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px 20px;
border-bottom: 1px solid #eee;
}
.title {
font-size: 16px;
font-weight: 600;
}
.close-btn {
cursor: pointer;
font-size: 20px;
color: #999;
}
.dialog-body {
padding: 20px;
}
.dialog-footer {
padding: 12px 20px;
border-top: 1px solid #eee;
text-align: right;
}
.btn {
padding: 6px 16px;
margin-left: 8px;
border-radius: 4px;
border: none;
cursor: pointer;
}
.cancel-btn {
background: #f5f5f5;
}
.confirm-btn {
background: #1677ff;
color: #fff;
}
</style>使用:
<template>
<BaseDialog
v-model:visible="dialogVisible"
title="編輯內(nèi)容"
size="middle"
:show-footer="true"
@confirm="handleSubmit"
>
這里是彈窗主體內(nèi)容
</BaseDialog>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import BaseDialog from '@/components/BaseDialog.vue'
const dialogVisible = ref(false)
const handleSubmit = () => {
console.log('點(diǎn)擊確定')
}
</script>重點(diǎn)拆解:為什么這是企業(yè)高級(jí)寫法
1. 類型規(guī)范
- 單獨(dú)
interface定義 Props、Emits - 用枚舉字面量
'small'|'middle'|'large',杜絕亂傳字符串 - 全程無(wú) any,所有參數(shù)都有類型約束
2. props 默認(rèn)值
用 withDefaults 給可選屬性設(shè)默認(rèn)值,TS 識(shí)別完美,不用自己邏輯判斷。
3. 事件規(guī)范
- 用
update:visible支持v-model:visible雙向綁定 - 事件參數(shù)類型嚴(yán)格約束,不會(huì)亂傳參
4. 多插槽規(guī)范
- 具名插槽
header/footer+ 默認(rèn)插槽 - 外部可自定義頭部、底部、內(nèi)容,復(fù)用性拉滿
5. defineExpose 暴露實(shí)例
父組件通過(guò) ref 可以直接調(diào)用組件內(nèi)部方法,適合復(fù)雜業(yè)務(wù)彈窗。
6. 自適應(yīng) + 配置化
通過(guò) size、width 靈活控制彈窗大小,適配不同業(yè)務(wù)場(chǎng)景。
Vue3 + TS 公共組件,固定遵守這 6 條
- 所有 Props 先用
interface定義,絕不寫行內(nèi)對(duì)象 - 可選屬性統(tǒng)一用
withDefaults給默認(rèn)值 - 固定值選項(xiàng)用字面量聯(lián)合類型,不用 string
- Emits 必須用接口約束事件名和參數(shù)
- 復(fù)雜配置抽
Record、computed統(tǒng)一管理 - 需要父組件調(diào)用方法,必須
defineExpose
三、配套全局類型規(guī)范(必加)
在項(xiàng)目 src 新建 types/global.d.ts
// 通用枚舉
export type StatusType = 0 | 1 | 2;
// 通用接口返回格式
export interface ResData<T> {
code: number;
data: T;
message: string;
}
// 常用工具類型復(fù)用
export type PartialOptional<T> = Partial<T>;總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Javascript 兩個(gè)窗體之間傳值實(shí)現(xiàn)代碼
眾所周知window.open() 函數(shù)可以用來(lái)打開(kāi)一個(gè)新窗口,那么如何在子窗體中向父窗體傳值呢,其實(shí)通過(guò)window.opener即可獲取父窗體的引用。2009-09-09
JS JSON對(duì)象轉(zhuǎn)為字符串的簡(jiǎn)單實(shí)現(xiàn)方法
這篇文章主要介紹了JS中JSON對(duì)象轉(zhuǎn)為字符串的簡(jiǎn)單實(shí)現(xiàn)方法。需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助2013-11-11
js+html+css實(shí)現(xiàn)手動(dòng)輪播和自動(dòng)輪播
這篇文章主要為大家詳細(xì)介紹了js+html+css實(shí)現(xiàn)手動(dòng)輪播和自動(dòng)輪播效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-12-12
js正則表達(dá)式驗(yàn)證密碼強(qiáng)度【推薦】
本文主要介紹了js利用正則表達(dá)式驗(yàn)證密碼強(qiáng)度的實(shí)例,具有很好的參考價(jià)值。下面跟著小編一起來(lái)看下吧2017-03-03
JS判斷字符串字節(jié)數(shù)并截取長(zhǎng)度的方法
這篇文章主要介紹了JS判斷字符串字節(jié)數(shù)并截取長(zhǎng)度的方法,涉及JavaScript針對(duì)頁(yè)面元素與字符串的動(dòng)態(tài)操作技巧,需要的朋友可以參考下2016-03-03
【經(jīng)驗(yàn)總結(jié)】編寫JavaScript代碼時(shí)應(yīng)遵循的14條規(guī)律
這篇文章主要介紹了編寫JavaScript代碼時(shí)應(yīng)遵循的14條規(guī)律,涉及javascript變量的定義,函數(shù)、表單、json的使用,邏輯運(yùn)算與頁(yè)面元素操作技巧等,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2016-06-06
javascript表單驗(yàn)證以及正則表達(dá)式舉例詳解
正則表達(dá)式描述了一種字符串匹配的模式,可以用來(lái)檢查一個(gè)串是否含有某種子串、將匹配的子串替換或者從某個(gè)串中取出符合某個(gè)條件的子串等,常用于表單驗(yàn)證等,下面這篇文章主要給大家介紹了關(guān)于javascript表單驗(yàn)證以及正則表達(dá)式的相關(guān)資料,需要的朋友可以參考下2023-05-05
js 創(chuàng)建對(duì)象 經(jīng)典模式全面了解
下面小編就為大家?guī)?lái)一篇js 創(chuàng)建對(duì)象 經(jīng)典模式全面了解。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-08-08

