Skip to main content
Version: Next

BaseTable Architecture Design

Table of Contents​

  1. Component Overview
  2. Architecture Design
  3. Core Modules
  4. Data Flow Design
  5. State Management
  6. Hook System
  7. Event System
  8. Rendering Mechanism
  9. Performance Optimization
  10. Extensibility Design

Component Overview​

BaseTable is a high-level data table component encapsulated based on Arco Design's Table component, offering rich functional features and flexible extension capabilities. This component adopts a modular design, achieving decoupling and reuse of functions through Composition API and Hook system.

Core Features​

  • Data Binding: Supports two-way binding of data sources, automatically syncing data changes
  • Column Configuration: Flexible column definitions supporting custom rendering, slots, styles, etc.
  • Pagination: Built-in pagination controls supporting remote data loading
  • Row Selection: Supports single/multi-selection with selection state management
  • Virtual Scrolling: Performance optimization for large dataset scenarios
  • Drag and Drop Sorting: Supports row dragging to adjust order
  • Auto Height: Adapts to container height
  • Internationalization: Complete multi-language support
  • Custom Rendering: Supports both Render functions and slots

Architecture Design​

BaseTable adopts a layered architecture design, dividing functionality into multiple independent modules, achieving collaboration between modules through dependency injection and Composition API.

Module Responsibilities​

ModuleFileResponsibility
Main ComponentBaseTable.tsxComponent entry, combining module functions
Props DefinitionProps.tsDefines component properties and events
Type DefinitionBaseTable.d.tsTypeScript type declarations
State ManagementuseState.tsComponent state initialization and sharing
Event HandlinguseEvent.tsUser interaction event handling
LifecycleuseHook.tsComponent Hooks and business logic
Column RenderinguseColumns.tsxColumn definitions and slot handling
UtilsuseUtils.tsGeneral utilities and internationalization
API Interfaceapi.tsData request and sorting interfaces
Auto TooltipAutoTooltip.tsxText overflow tooltip component
Event Definitionevents.tsList of custom events

Core Modules​

1. Props System (Props.ts)​

The Props module defines all input properties and events of the component, using Vue 3's PropType for type constraints.

Core Property Categories:

// Data Related
- columns: Column configuration array
- dataSource: Data source
- url: Remote data URL
- dataTransformer: Data transformation function

// Display Control
- isSeq: Whether to show sequence number
- isHidePagination: Whether to hide pagination
- isVScroll: Whether to enable virtual scrolling
- autoHeight: Whether to enable auto height

// Interaction
- isChecked: Whether to support selection
- isDrag: Whether to support drag and drop
- radio: Whether in single selection mode
- columnResizable: Whether column width is resizable

// Style Customization
- cellStyle: Cell style
- rowStyle: Row style
- rowClassName: Row class name
- hightLightRow: Highlight row color

// Internationalization
- LanguageScopeKey: Language scope key

2. State Management (useState.ts)​

The state management module uses Vue 3's provide/inject mechanism to implement state sharing, ensuring all sub-modules can access global state.

State Structure:

Dependency Injection Pattern:

// Create State
export const createState = (props, ctx) => {
const store = useStore(props, ctx)
provide(KEY, store) // Inject into child components
return store
}

// Use State
export const injectState = () => {
return inject(KEY) as StoreType
}

3. Event Handling (useEvent.ts)​

The event module is responsible for handling user interactions, including row clicks, column resizing, row selection, etc.

Event Handling Flow:

Core Events:

Event NameTrigger TimingParameters
onRowClickRow Clickrecord: TableData
onColumnResizeColumn ResizedataIndex: string, width: number
onSelectSingle Row SelectselectedRowKeys, selectedRows
onSelectAllSelect All/Deselect AllisCheck: boolean

4. Lifecycle Hook (useHook.ts)​

The Hook module manages component lifecycle logic and business functionality.

Core Functions:

  1. Auto Height Calculation: Use ResizeObserver to listen for container size changes
  2. Data Loading: Remote data fetching and local data updates
  3. Row Highlight: Visual feedback for the current row
  4. Pagination Handling: Pagination parameter management
// Auto Height Calculation
onMounted(() => {
const resizeObserver = new ResizeObserver(debounce((entries) => {
const height = entries[0].borderBoxSize[0].blockSize
currentHeight.value = height
}, 150))
resizeObserver.observe(baseTableRef.value)
})

// Data Loading
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. Column Rendering System (useColumns.tsx)​

The column rendering module handles the rendering logic of table columns, supporting both slots and Render functions.

Rendering Priority:

Rendering Logic:

const RenderSlot = ({ column, record, rowIndex }) => {
const cellStyle = getCellStyle({ column, record, rowIndex })

// Priority 1: Slot Rendering
if (ctx.slots[column.field]) {
return <div class={styles.bodyCell} style={cellStyle}>
{ctx.slots[column.field]({ column, record, rowIndex })}
</div>
}

// Priority 2: Render Function
const render = column.customRender ||
(() => <AutoTooltip text={getValue(record, column.field)} />)

return <div class={styles.bodyCell} style={cellStyle}>
{render({ column, record, rowIndex })}
</div>
}

Data Flow Design​

BaseTable's data flow combines unidirectional data flow with two-way binding.

Data Loading Process​

  1. Initialization: Props pass initial data source
  2. Reactive Binding: Use useVModels for two-way binding
  3. Remote Loading: Auto load configured via url property
  4. Data Transformation: dataTransformer preprocesses data
  5. State Update: Update dataSource and totalCount
  6. Trigger Event: Emit reload event to notify parent component

Performance Optimized Data Mapping​

// Use Map to improve lookup performance
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) Time complexity to get records
const getRecordsByKeys = (keys: string[]) => {
return keys.map(key => dataSourceMap.value.get(key)).filter(Boolean)
}

State Management​

State Categories​

BaseTable states can be categorized as follows:

State Synchronization Mechanism​

// 1. Parent-Child Sync - useVModels
const { dataSource } = useVModels(props, ctx.emit)

// 2. Cross-Module Sync - provide/inject
provide(KEY, store)
const store = inject(KEY)

// 3. Selection State Sync
watch(selectedKeys, async () => {
await nextTick()
const records = getRecordsByKeys(selectedKeys.value)
emit('check', records)
})

Hook System​

BaseTable's Hook system handles component side effects and business logic.

Core Hook Functions​

1. Auto Height Hook​

const autoHeight = computed(() => {
if (baseTableRef.value) {
const rect = baseTableRef.value.getBoundingClientRect()
const rectHeight = currentHeight.value || rect.height
return rectHeight > 42 ? rectHeight - 42 : rectHeight
}
})

Working Principle:

  • Use ResizeObserver to listen for container size changes
  • Debounce to avoid frequent calculations
  • Subtract header height (42px) to get content area height
  • Used in conjunction with virtual scrolling

2. Data Loading 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. Row Highlight 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
}
}

Event System​

Event Categories​

BaseTable supports two types of events:

  1. Standard Events (from Arco Table)

    • select: Row selection
    • selectAll: Select all
    • rowClick: Row click
    • columnResize: Column width resize
  2. Custom Events (BaseTable extensions)

    • update:dataSource: Data source update
    • load: Before data load
    • beforeLoad: During data load
    • reload: After data load
    • check: Selection state change

Event Handling Chain​

Event Debouncing​

// Debounce column resize
const onColumnResize = debounce((dataIndex: string, width: number) => {
emit('columnResize', dataIndex, width)
}, 200)

Rendering Mechanism​

Rendering Process​

Slot System​

BaseTable provides a flexible slot system:

const slotMap = {
// Sequence column
seq: columnSeq.value.seq,

// Dynamic column slots
...slots,

// Empty data slot
empty: () => (
<div class={styles.nullData}>{_t('No Data')}</div>
)
}

Usage Example:

<BaseTable :columns="columns" :dataSource="data">
<template #name="{ record }">
<a-link>{{ record.name }}</a-link>
</template>
</BaseTable>

AutoTooltip Component​

Automatic text overflow tooltip component, used for default rendering scenarios.

Working Principle:

  1. Detect text overflow on mount (scrollWidth > clientWidth)
  2. Listen for window resize events to re-detect
  3. Use Arco Tooltip to show full text when overflow occurs
  4. Show text directly when no overflow
const checkOverflow = () => {
if (containerRef.value) {
isOverflow.value =
containerRef.value.scrollWidth > containerRef.value.clientWidth
}
}

onMounted(() => {
checkOverflow()
window.addEventListener('resize', checkOverflow)
})

Performance Optimization​

1. Virtual Scrolling​

Enable virtual scrolling via isVScroll, rendering only visible rows.

virtual-list-props={
props.isVScroll ? { height: autoHeight.value } : undefined
}

Applicable Scenario: Data volume > 1000 rows

2. Data Mapping Optimization​

Use Map structure to store data, improving lookup efficiency:

// 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. Event Debouncing​

Debounce high-frequency events:

// Column resize
const onColumnResize = debounce((dataIndex, width) => {
emit('columnResize', dataIndex, width)
}, 200)

// Container resize listener
const fn = debounce((entries) => {
currentHeight.value = entries[0].borderBoxSize[0].blockSize
}, 150)

4. Computed Property Caching​

Use computed to cache calculation results:

const columns = computed(() => {
return props.columns.map(item => ({
...item,
title: _t(item.title)
}))
})

5. Conditional Rendering​

Render functions on demand based on configuration:

// Pagination
pagination={props.isHidePagination ? false : {...}}

// Selection
row-selection={props.isChecked ? rowSelection : undefined}

// Drag and Drop
draggable={props.isDrag ? { type: 'handle', width: 20 } : undefined}

Extensibility Design​

1. Plugin-based Architecture​

BaseTable uses modular design, with independent pluggable functional modules:

// Core modules
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 Function Extension​

Supports custom render functions:

columns: [
{
field: 'status',
title: 'Status',
customRender: ({ record }) => (
<a-tag color={record.status === 1 ? 'green' : 'red'}>
{record.status === 1 ? 'Enabled' : 'Disabled'}
</a-tag>
)
}
]

3. Style Customization​

Multi-level style customization capabilities:

// 1. Row style
rowStyle: (record) => ({ background: record.isNew ? '#f0f9ff' : '' })

// 2. Cell style
cellStyle: ({ record, column }) => ({
color: record.amount > 1000 ? 'red' : 'inherit'
})

// 3. Row class name
rowClassName: (record) => record.disabled ? 'disabled-row' : ''

// 4. Highlight row color
hightLightRow: '#e6f7ff'

4. Data Transformer​

Supports data preprocessing:

<BaseTable
url="/api/users"
:dataTransformer="(data) => data.map(item => ({
...item,
fullName: `${item.firstName} ${item.lastName}`
}))"
/>

5. Internationalization Support​

Complete multi-language support:

// Auto translate column titles
columns: [
{ field: 'name', title: 'Name' } // Auto translated
]

// Support scoped translation
<BaseTable LanguageScopeKey="ProductManagement" />

6. Event Extension​

Supports custom event listening:

// events.ts
export const events = [
'update:dataSource',
'load',
'beforeLoad',
'reload'
]

// Usage
<BaseTable
@reload="handleReload"
@check="handleCheck"
@select="handleSelect"
/>

Best Practices​

1. Basic Usage​

<template>
<BaseTable
:columns="columns"
:dataSource="dataSource"
:isChecked="true"
@check="handleCheck"
/>
</template>

<script setup>
const columns = [
{ field: 'id', title: 'ID', width: 80 },
{ field: 'name', title: 'Name', width: 120 },
{ field: 'age', title: 'Age', width: 80 }
]

const dataSource = ref([
{ id: 1, name: 'John Doe', age: 25 },
{ id: 2, name: 'Jane Doe', age: 30 }
])

const handleCheck = (records) => {
console.log('Selected records:', records)
}
</script>

2. Remote Data Loading​

<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 ? 'Enabled' : 'Disabled'
}))
}
</script>

3. Custom Rendering​

<template>
<BaseTable :columns="columns" :dataSource="dataSource">
<template #status="{ record }">
<a-tag :color="record.status === 1 ? 'green' : 'red'">
{{ record.status === 1 ? 'Enabled' : 'Disabled' }}
</a-tag>
</template>

<template #actions="{ record }">
<a-space>
<a-button size="mini" @click="handleEdit(record)">Edit</a-button>
<a-button size="mini" status="danger" @click="handleDelete(record)">
Delete
</a-button>
</a-space>
</template>
</BaseTable>
</template>

4. Virtual Scrolling + Auto Height​

<template>
<BaseTable
:columns="columns"
:dataSource="largeDataSource"
:isVScroll="true"
:autoHeight="true"
style="height: 500px"
/>
</template>

5. Drag and Drop Sorting​

<template>
<BaseTable
:columns="columns"
:dataSource="dataSource"
:isDrag="true"
@change="handleDragSort"
/>
</template>

<script setup>
const handleDragSort = (data) => {
// Update order
console.log('New order:', data)
}
</script>

Summary​

BaseTable is a powerful, highly extensible table component with the following characteristics:

Design Advantages​

  1. Modular Architecture: Functionality decoupled, easy to maintain and extend
  2. Performance Optimization: Multiple optimizations including virtual scrolling, data mapping, event debouncing
  3. Type Safety: Complete TypeScript type definitions
  4. Flexible Rendering: Supports slots, Render functions, default rendering
  5. State Management: Uses provide/inject for cross-module state sharing
  6. Internationalization: Complete multi-language support
  7. Responsive: Auto height, container size listening, etc.

Technical Highlights​

  • Composition API: Fully utilizes Vue 3 Composition API
  • Hook System: Logic reuse and separation of concerns
  • Dependency Injection: Elegant state sharing mechanism
  • Performance Optimization: Virtual scrolling, computed caching, event debouncing
  • Extensibility: Plugin-based design, easy to extend functions

BaseTable provides a unified, efficient, and easy-to-use table solution for the project and is an indispensable core UI component in the entire system.