BaseTable 架构设计
目录
组件概述
BaseTable 是基于 Arco Design 的 Table 组件封装的高级数据表格组件,提供了丰富的功能特性和灵活的扩展能力。该组件采用模块化设计,通过组合式 API 和 Hook 系统实现了功能的解耦与复用。
核心特性
- 数据绑定: 支持双向绑定数据源,自动同步数据变化
- 列配置: 灵活的列定义,支持自定义渲染、插槽、样式等
- 分页功能: 内置分页控件,支持远程数据加载
- 行选择: 支持单选/多选,提供选择状态管理
- 虚拟滚动: 大数据量场景下的性能优化
- 拖拽排序: 支持行拖拽调整顺序
- 自动高度: 自适应容器高度
- 国际化: 完整的多语言支持
- 自定义渲染: 支持 Render 函数和插槽两种方式
架构设计
BaseTable 采用分层架构设计,将功能划分为多个独立的模块,通过依赖注入和组合式 API 实现模块间的协作。
模块职责
模块 | 文件 | 职责 |
---|---|---|
主组件 | BaseTable.tsx | 组件入口,组合各模块功能 |
属性定义 | Props.ts | 定义组件属性和事件 |
类型定义 | BaseTable.d.ts | TypeScript 类型声明 |
状态管理 | useState.ts | 组件状态初始化和共享 |
事件处理 | useEvent.ts | 用户交互事件处理 |
生命周期 | useHook.ts | 组件Hook和业务逻辑 |
列渲染 | useColumns.tsx | 列定义和插槽处理 |
工具 函数 | useUtils.ts | 通用工具和国际化 |
API接口 | api.ts | 数据请求和排序接口 |
自动提示 | AutoTooltip.tsx | 文本溢出提示组件 |
事件定义 | events.ts | 自定义事件列表 |
核心模块
1. Props 属性系统 (Props.ts)
Props 模块定义了组件的所有输入属性和事件,采用 Vue 3 的 PropType 进行类型约束。
核心属性分类:
// 数据相关
- columns: 列配置数组
- dataSource: 数据源
- url: 远程数据地址
- dataTransformer: 数据转换函数
// 显示控制
- isSeq: 是否显示序号
- isHidePagination: 是否隐藏分页
- isVScroll: 是否启用虚拟滚动
- autoHeight: 是否自动高度
// 交互功能
- isChecked: 是否支持选择
- isDrag: 是否支持拖拽
- radio: 是否单选模式
- columnResizable: 列宽可调整
// 样式定制
- cellStyle: 单元格样式
- rowStyle: 行样式
- rowClassName: 行类名
- hightLightRow: 高亮行颜色
// 国际化
- LanguageScopeKey: 语言作用域
2. 状态管理 (useState.ts)
状态管理模块使用 Vue 3 的 provide/inject 机制实现状态共享,确保所有子模块都能访问全局状态。
状态结构:
依赖注入模式:
// 创建状态
export const createState = (props, ctx) => {
const store = useStore(props, ctx)
provide(KEY, store) // 注入到子组件
return store
}
// 使用状态
export const injectState = () => {
return inject(KEY) as StoreType
}
3. 事件处理 (useEvent.ts)
事件模块负责处理用户交互,包括行点击、列调整、行选择等操作。
事件处理流程:
核心事件:
事件名 | 触发时机 | 参数 |
---|---|---|
onRowClick | 行点击 | record: TableData |
onColumnResize | 列宽调整 | dataIndex: string, width: number |
onSelect | 单行选择 | selectedRowKeys, selectedRows |
onSelectAll | 全选/反选 | isCheck: boolean |
4. 生命周期 Hook (useHook.ts)
Hook 模块管理组件的生命周期逻辑和业务功能。
核心功能:
- 自动高度计算: 使用 ResizeObserver 监听容器大小变化
- 数据加载: 远程数据获取和本地数 据更新
- 行高亮: 当前行的视觉反馈
- 分页处理: 分页参数管理
// 自动高度计算
onMounted(() => {
const resizeObserver = new ResizeObserver(debounce((entries) => {
const height = entries[0].borderBoxSize[0].blockSize
currentHeight.value = height
}, 150))
resizeObserver.observe(baseTableRef.value)
})
// 数据加载
const getList = async (query = {}) => {
const params = { ...baseParams.value, ...query }
const res = await getListData(params, props.url)
dataSource.value = props.dataTransformer ?
props.dataTransformer(res.items) : res.items
totalCount.value = res.totalCount
}
5. 列渲染系统 (useColumns.tsx)
列渲染模块处理表格列的渲染逻辑,支持插槽和 Render 函数两种方式。
渲染优先级:
渲染逻辑:
const RenderSlot = ({ column, record, rowIndex }) => {
const cellStyle = getCellStyle({ column, record, rowIndex })
// 优先级1: 插槽渲染
if (ctx.slots[column.field]) {
return <div class={styles.bodyCell} style={cellStyle}>
{ctx.slots[column.field]({ column, record, rowIndex })}
</div>
}
// 优先级2: Render函数
const render = column.customRender ||
(() => <AutoTooltip text={getValue(record, column.field)} />)
return <div class={styles.bodyCell} style={cellStyle}>
{render({ column, record, rowIndex })}
</div>
}
数据流设计
BaseTable 的数据流采用单向数据流和双向绑定相结合的模式。
数据加载流程
- 初始化: Props 传入初始数据源
- 响应式绑定: 使用
useVModels
实现双向绑定 - 远程加载: 通过
url
属性配置自动加载 - 数据转换:
dataTransformer
预处理数据 - 状态更新: 更新
dataSource
和totalCount
- 触发事件: 发射
reload
事件通知父组件
性能优化的数据映射
// 使用 Map 提升查找性能
const dataSourceMap = computed(() => {
const map = new Map<string, any>()
for (const item of dataSource.value) {
map.set(item[rowId.value], item)
}
return map
})
// O(1) 时间复杂度获取记录
const getRecordsByKeys = (keys: string[]) => {
return keys.map(key => dataSourceMap.value.get(key)).filter(Boolean)
}
状态管理
状态分类
BaseTable 的状态可分为以下几类:
状态同步机制
// 1. 父子组件同步 - useVModels
const { dataSource } = useVModels(props, ctx.emit)
// 2. 跨模块同步 - provide/inject
provide(KEY, store)
const store = inject(KEY)
// 3. 选择状态同步
watch(selectedKeys, async () => {
await nextTick()
const records = getRecordsByKeys(selectedKeys.value)
emit('check', records)
})
Hook系统
BaseTable 的 Hook 系统负责处理组件的副作 用和业务逻辑。
核心 Hook 功能
1. 自动高度 Hook
const autoHeight = computed(() => {
if (baseTableRef.value) {
const rect = baseTableRef.value.getBoundingClientRect()
const rectHeight = currentHeight.value || rect.height
return rectHeight > 42 ? rectHeight - 42 : rectHeight
}
})
工作原理:
- 使用
ResizeObserver
监听容器尺寸变化 - 防抖处理避免频繁计算
- 减去表头高度 (42px) 得到内容区域高度
- 配合虚拟滚动使用
2. 数据加载 Hook
const getList = async (query = {}) => {
if (!props.url) return
const params = { ...baseParams.value, ...query }
const res = await getListData(params, props.url)
dataSource.value = props.dataTransformer ?
props.dataTransformer(res.items || res) :
res.items || res
totalCount.value = res.totalCount || res.length
ctx.emit('reload')
await nextTick()
return dataSource.value
}
3. 行高亮 Hook
const getRowClassName = (record: TableData, rowIndex: number) => {
if (record === currentRow.value) {
return styles.baseTableRow
}
}
const setCurrentRow = (key: string) => {
const row = dataSource.value.find(item => item[rowId.value] === key)
if (row) {
currentRow.value = row
}
}
事件系统
事件分类
BaseTable 支持两类事件:
-
标准事件 (来自 Arco Table)
- select: 行选择
- selectAll: 全选
- rowClick: 行点击
- columnResize: 列宽调整
-
自定义事件 (BaseTable 扩展)
- update:dataSource: 数据源更新
- load: 数据加载前
- beforeLoad: 数据加载中
- reload: 数据加载后
- check: 选择状态变化
事件处理链
事件去抖处理
// 列宽调整使用防抖
const onColumnResize = debounce((dataIndex: string, width: number) => {
emit('columnResize', dataIndex, width)
}, 200)
渲染机制
渲染流程
插槽系统
BaseTable 提供了灵活的插槽系统:
const slotMap = {
// 序号列
seq: columnSeq.value.seq,
// 动态列插槽
...slots,
// 空数据插槽
empty: () => (
<div class={styles.nullData}>{_t('暂无数据')}</div>
)
}
使用示例:
<BaseTable :columns="columns" :dataSource="data">
<template #name="{ record }">
<a-link>{{ record.name }}</a-link>
</template>
</BaseTable>
AutoTooltip 组件
自动文本溢出提示组件,用于默认渲染场景。
工作原理:
- 挂载时检测文本是否溢出 (
scrollWidth > clientWidth
) - 监听窗口 resize 事件重新检测
- 溢出时使用 Arco Tooltip 显示完整文本
- 未溢出时直接显示文本
const checkOverflow = () => {
if (containerRef.value) {
isOverflow.value =
containerRef.value.scrollWidth > containerRef.value.clientWidth
}
}
onMounted(() => {
checkOverflow()
window.addEventListener('resize', checkOverflow)
})
性能优化
1. 虚拟滚动
通过 isVScroll
开启虚拟滚动,只渲染可见区域的行。
virtual-list-props={
props.isVScroll ? { height: autoHeight.value } : undefined
}
适用场景: 数据量 > 1000 行
2. 数据映射优化
使用 Map 结构存储数据,提升查找效率:
// O(n) -> O(1)
const dataSourceMap = computed(() => {
const map = new Map()
for (const item of dataSource.value) {
map.set(item[rowId.value], item)
}
return map
})
3. 事件防抖
对高频事件进行防抖处理:
// 列宽调整
const onColumnResize = debounce((dataIndex, width) => {
emit('columnResize', dataIndex, width)
}, 200)
// 容器尺寸监听
const fn = debounce((entries) => {
currentHeight.value = entries[0].borderBoxSize[0].blockSize
}, 150)
4. 计算属性缓存
使用 computed 缓存计算结果:
const columns = computed(() => {
return props.columns.map(item => ({
...item,
title: _t(item.title)
}))
})
5. 条件渲染
根据配置按需渲染功能:
// 分页
pagination={props.isHidePagination ? false : {...}}
// 选择
row-selection={props.isChecked ? rowSelection : undefined}
// 拖拽
draggable={props.isDrag ? { type: 'handle', width: 20 } : undefined}
扩展性设计
1. 插件化架构
BaseTable 采用模块化设计,各功能模块独立可插拔:
// 核心模块
const { columns, dataSource, selectedKeys } = createState(props, ctx)
const events = useEvent(props, ctx)
const { getRowClassName, getTableStyle } = useHook(props, ctx)
const slots = ColumnSlots(props, ctx, columns)
2. Render 函数扩展
支持自定义渲染函数:
columns: [
{
field: 'status',
title: '状态',
customRender: ({ record }) => (
<a-tag color={record.status === 1 ? 'green' : 'red'}>
{record.status === 1 ? '启用' : '禁用'}
</a-tag>
)
}
]
3. 样式定制
多层次样式定制能力:
// 1. 行样式
rowStyle: (record) => ({ background: record.isNew ? '#f0f9ff' : '' })
// 2. 单元格样式
cellStyle: ({ record, column }) => ({
color: record.amount > 1000 ? 'red' : 'inherit'
})
// 3. 行类名
rowClassName: (record) => record.disabled ? 'disabled-row' : ''
// 4. 高亮行颜色
hightLightRow: '#e6f7ff'
4. 数据转换器
支持数据预处理:
<BaseTable
url="/api/users"
:dataTransformer="(data) => data.map(item => ({
...item,
fullName: `${item.firstName} ${item.lastName}`
}))"
/>
5. 国际化支持
完整的多语言支持:
// 自动翻译列标题
columns: [
{ field: 'name', title: '姓名' } // 自动翻译
]
// 支持作用域翻译
<BaseTable LanguageScopeKey="ProductManagement" />
6. 事件扩展
支持自定义事件监听:
// events.ts
export const events = [
'update:dataSource',
'load',
'beforeLoad',
'reload'
]
// 使用
<BaseTable
@reload="handleReload"
@check="handleCheck"
@select="handleSelect"
/>
最佳实践
1. 基础使用
<template>
<BaseTable
:columns="columns"
:dataSource="dataSource"
:isChecked="true"
@check="handleCheck"
/>
</template>
<script setup>
const columns = [
{ field: 'id', title: 'ID', width: 80 },
{ field: 'name', title: '姓名', width: 120 },
{ field: 'age', title: '年龄', width: 80 }
]
const dataSource = ref([
{ id: 1, name: '张三', age: 25 },
{ id: 2, name: '李四', age: 30 }
])
const handleCheck = (records) => {
console.log('选中的记录:', records)
}
</script>
2. 远程数据加载
<template>
<BaseTable
url="/api/users"
:columns="columns"
:pageSize="20"
:dataTransformer="transformData"
/>
</template>
<script setup>
const transformData = (data) => {
return data.map(item => ({
...item,
statusText: item.status === 1 ? '启用' : '禁用'
}))
}
</script>
3. 自定义渲染
<template>
<BaseTable :columns="columns" :dataSource="dataSource">
<template #status="{ record }">
<a-tag :color="record.status === 1 ? 'green' : 'red'">
{{ record.status === 1 ? '启用' : '禁用' }}
</a-tag>
</template>
<template #actions="{ record }">
<a-space>
<a-button size="mini" @click="handleEdit(record)">编辑</a-button>
<a-button size="mini" status="danger" @click="handleDelete(record)">
删除
</a-button>
</a-space>
</template>
</BaseTable>
</template>
4. 虚拟滚动 + 自动高度
<template>
<BaseTable
:columns="columns"
:dataSource="largeDataSource"
:isVScroll="true"
:autoHeight="true"
style="height: 500px"
/>
</template>
5. 拖拽排序
<template>
<BaseTable
:columns="columns"
:dataSource="dataSource"
:isDrag="true"
@change="handleDragSort"
/>
</template>
<script setup>
const handleDragSort = (data) => {
// 更新排序
console.log('新的排序:', data)
}
</script>
总结
BaseTable 是一个功能强大、高度可扩展的表格组件,具有以下特点:
设计优势
- 模块化架构: 功能解耦,易于维护和扩展
- 性能优化: 虚拟滚动、数据映射、事件防抖等多重优化
- 类型安全: 完整的 TypeScript 类型定义
- 灵活渲染: 支持插槽、Render函数、默认渲染多种方式
- 状态管理: 使用 provide/inject 实现跨模块状态共享
- 国际化: 完整的多语言支持
- 响应式: 自动高度、容器尺寸监听等响应式特性
技术亮点
- Composition API: 充分利用 Vue 3 组合式 API
- Hook 系统: 逻辑复用和关注点分离
- 依赖注入: 优雅的状态共享机制
- 性能优化: 虚拟滚动、计算缓存、事件防抖
- 扩展性: 插件化设计,易于功能扩展
BaseTable 为项目提供了统一、高效、易用的表格解决方案,是整个系统中不可或缺的核心UI组件。