【开源推广】 开源一个 Vue 3 Table:API 学 antdv、主题学 Nuxt UI

本帖使用社区开源推广,符合推广要求。我申明并遵循社区要求的以下内容: 我的帖子已经打上 开源推广 标签: 是 我的开源项目完整开源,无未开源部分: 是 我的开源项目已链接认可 LINUX DO 社区: 是 我帖子内的项目介绍,AI生成、润色内容部分已截图发出: 是 以上选择我承诺是永久有效的,接受社...
【开源推广】 开源一个 Vue 3 Table:API 学 antdv、主题学 Nuxt UI
【开源推广】 开源一个 Vue 3 Table:API 学 antdv、主题学 Nuxt UI
本帖使用社区开源推广,符合推广要求。我申明并遵循社区要求的以下内容:
  • 我的帖子已经打上 开源推广 标签:
  • 我的开源项目完整开源,无未开源部分:
  • 我的开源项目已链接认可 LINUX DO 社区:
  • 我帖子内的项目介绍,AI生成、润色内容部分已截图发出:
  • 以上选择我承诺是永久有效的,接受社区和佬友监督:

以下为项目介绍正文内容,AI生成、润色内容已使用截图方式发出


项目地址:GitHub - parade0393/vtable-guild · GitHub

首先说明:这是一个 vibe coding 的项目。大部分代码由 AI 协作产出,把关、调试、痛点收集都是我自己做的。下面要聊的所有技术细节都能在仓库里翻到对应源码。

一、这是一个什么组件

  • 基于 Vue 3 Composition API + TSX,全部子组件用 TSX 写
  • 基于 Tailwind CSS 4 + @tailwindcss/vite
  • 三层主题模型:默认预设 → 全局配置 → 实例 props,颗粒度细到单个 slot
  • 当前内置两套预设:antdv(默认)和 element-plus,运行时可切换
  • 该有的能力都有:虚拟滚动、固定列、固定表头、树形、行展开、行选择、排序筛选、列拖拽 resize、表头分组、自定义渲染、国际化

二、为什么会有这个项目

经常做中后台管理系统,table 是高频组件。

ant-design-vue

  1. 不支持虚拟滚动。几千行数据就开始明显卡。
  2. 滚动条恶心。固定表头 + bordered 的时候,表头会多出一列对齐滚动条的空白列,强迫症看着难受。
  3. 斑马纹没有开箱支持。要自己写 CSS。
  4. 改样式不方便。要么覆盖 className 跟权重打架,要么用 ConfigProvider 但粒度太粗,要么改 less 变量改一片连带影响。

element-plus

  1. 不支持虚拟滚动el-table 没有,el-table-v2 支持但 API 是另一套——换组件名 + 重新写 column 定义,老项目想加虚拟滚动等于换轮子。
  2. 不支持列配置驱动。所有列必须用 <el-table-column> 写在模板里,动态列要么 v-for 一堆,要么 JSX 绕路。
  3. UI 不如 ant-design-vue。这个见仁见智,但我们团队的偏好是 antdv。

vxe-table

  1. 太重。功能确实多,但要引入它的整套生态,单独一个 table 的成本太大。
  2. 样式不兼容。和现有系统的 UI 风格不一致,定制起来同样费劲。

tanstack-table

  1. headless。本身是个无 UI 的「状态机」,要做大量封装才能用起来。如果项目里其它组件都是有 UI 的,单独引入一个 headless 又得自己实现一整套表头/单元格/筛选/排序的 UI。

所以做了 vtable-guild,目标是补齐这些缺失,但不重新发明轮子——API 保留 antdv 风味,老项目最小改动接入;样式系统重新做,让定制变得简单。

三、vtable-guild 的特色

3.1 能用它做什么 —— 一张能力矩阵

下面这些能力当前都已实现,可以在 playground(pnpm playground)找到对应的对照演示页。

能力 说明 antdv 对齐 排序(单列 / 多列) controlled (sortOrder) + uncontrolled (defaultSortOrder) ✓ 筛选(菜单 / 树形 / 搜索 / 完全自定义 dropdown) filterModefilterSearchfilterDropdown ✓ 行选择 rowSelection (checkbox / radio),支持树形联动 ✓ 树形数据 expandedRowKeys / defaultExpandedRowKeys / childrenColumnName ✓ 行展开(非树形) expandable.expandedRowRender ✓ 固定列 / 固定表头 column.fixed + scroll.y + sticky ✓ 表头分组 column.children ✓ 自定义渲染 customRender / customCell / customHeaderCell ✓ 局部化 内置 zh-CN / en-US + localeOverrides ✓ 列拖拽 resize column.resizable + minWidth / maxWidth 约束 ✓ 虚拟滚动 virtual + scroll.y 显式启用 独有 主题预设切换 themePreset: 'antdv' | 'element-plus' 独有

3.2 迁移成本:从 antdv Table 切过来,column 配置一行不改

这个组件最大的「不挑食」体现在 API 上——column 配置、change 事件、rowSelectionexpandablescrollstickysize 这些 prop 的命名和语义全部对齐 ant-design-vue。

实际迁移的工作量大概是这样:

- import { Table } from 'ant-design-vue'
+ import { VTable } from '@vtable-guild/vtable-guild'

- <a-table :columns="columns" :data-source="data" @change="onChange" />
+ <VTable :columns="columns" :data-source="data" @change="onChange" />

columns 数组里的对象、onChange 里的参数解构,理论上都不需要动。

唯一需要注意的差异:虚拟滚动需要显式开启 virtual=true + scroll.y,不是大数据量自动启用。

3.3 主题随便改:三层覆盖 + CSS 变量驱动 + 预设切换

这是 vtable-guild 最想解决的痛点,也是花心思最多的地方。

三层主题,从粗到细

层 1:选预设——99% 场景这一层就够了。

// main.ts
import { createApp } from 'vue'
import { createVTableGuild } from '@vtable-guild/vtable-guild'
import '@vtable-guild/vtable-guild/css'

const app = createApp(App)
app.use(createVTableGuild({ themePreset: 'antdv' }))  // 或 'element-plus'

层 2:全局批量覆盖某个 slot——想统一改所有表格的表头样式?这一层。

app.use(createVTableGuild({
  themePreset: 'antdv',
  theme: {
    table: {
      slots: {
        th: 'bg-slate-900 text-white',
        td: 'text-slate-700',
      },
      defaultVariants: { size: 'middle' },
    },
  },
}))

这里出现了一个关键概念:slots。tailwind-variants 的核心是把一个组件拆成多个命名 slot——比如表格的 root / table / thead / tbody / tr / th / td,再加上排序、筛选、空态等等。每个 slot 各自一份 class 字符串。

层 3:单实例覆盖——只想改这一个表格的某个 slot。

<template>
  <VTable
    :columns="columns"
    :data-source="data"
    :ui="{ th: 'bg-blue-50', tr: 'hover:bg-blue-50/40' }"
    class="rounded-2xl shadow-sm"
  />
</template>

ui prop 按 slot 名覆盖,class 自动落到 root slot。

CSS 变量驱动亮暗切换

CSS 文件里有三层:语义 token → 组件 token → slots class 引用组件 token

/* packages/theme/css/presets/antdv.css */
:where(:root),
[data-vtg-preset='antdv'] {
  /* 语义 token */
  --color-surface: #ffffff;
  --color-on-surface: rgb(0 0 0 / 88%);
  --color-default: #f0f0f0;
  --color-primary: #1677ff;

  /* 组件 token,100% 引用语义 token */
  --vtg-table-bg: var(--color-surface);
  --vtg-table-header-bg: var(--color-surface-hover);
  --vtg-table-border-color: var(--color-default);
}

.dark [data-vtg-preset='antdv'],
[data-vtg-preset='antdv'].dark {
  /* 暗色只覆盖语义 token,组件 token 自动跟随 */
  --color-surface: #141414;
  --color-on-surface: rgb(255 255 255 / 85%);
}

切暗色模式只要给 <html>.dark,所有 slots 自动响应。要切 element-plus 预设,把 data-vtg-preset 改成 element-plus 即可。

一句话总结这一节:想从 antdv 风切到 element-plus 风?改一个字符串。想统一全局改?传 theme 配置。想改单个表格?用 ui prop。想做完全自己的视觉?写一个 preset 对象。

3.4 大数据不卡:虚拟滚动 + 树形

虚拟滚动的接入

开启方式就一行:

<VTable virtual :scroll="{ y: 400 }" :columns="columns" :data-source="bigData" />

columns 定义、排序筛选、固定列、行选择,全都和不开虚拟滚动时一样——这是和 element-plus v2 路线最大的区别:不是另一个组件、另一套 API,而是同一个 <VTable> 加一个 virtual prop。

组件的效果图如下

image
image
image

1 个帖子 - 1 位参与者

阅读完整话题

来源: LinuxDo 最新话题查看原文