- UI组件
- 前端
【免费下载链接】varlet
A Vue3 component library based on Material Design 2 and 3, supporting mobile and desktop.
Varlet 的Table是一个刻意保持"极简"的数据表格组件:它不解析列配置、不处理排序筛选,只负责用 Material Design 的视觉语言把你写的原生<table>结构包裹成一张带卡片阴影、行悬停、滚动容器的标准表格。读完本文,你将掌握var-table的全部属性(full-width、elevation、plain、bordered、surface、scroller-height)与两个插槽的实际用法,理解固定表头背后的overflow+sticky实现原理,并能通过样式变量完成精细的视觉定制。
组件的整体结构
在动手写模板之前,先看一下 Table 在仓库中的组成(所有路径均相对于仓库根目录):
| 文件 | 作用 |
|---|---|
| packages/varlet-ui/src/table/Table.vue | 组件主文件,定义渲染结构与类名逻辑 |
| packages/varlet-ui/src/table/props.ts | 全部 Prop 的类型定义与默认值 |
| packages/varlet-ui/src/table/table.less | 样式实现与全部 CSS 变量默认值 |
| packages/varlet-ui/src/table/index.ts | 安装逻辑,并暴露tableProps供全局默认值配置 |
| packages/varlet-ui/types/table.d.ts | TypeScript 类型声明(TableProps、Table) |
| packages/varlet-ui/src/table/tests/index.spec.js | 各 Prop 与插槽的单元/挂载测试 |
| packages/varlet-ui/src/table/example/index.vue | 官方文档站对应的完整示例页 |
从源码结构看,Table的模板只有三层结构:最外层.var-table容器负责卡片形态(阴影、圆角、plain/bordered/surface-low 修饰),中间的.var-table__main是滚动容器(承载scroller-height),内部的<table class="var-table__table">通过默认插槽渲染用户提供的thead/tbody;若存在footer插槽,再在底部追加一个.var-table__footer区块。组件本身对单元格没有任何逻辑介入,排序、选择、展开等能力需要业务层自行组合,这也是它"极简"定位的体现。
[index.ts](https://link.gitcode.com/i/18ff7284daac3d87d5ebd24973ec0131)中除了标准的withInstall(Table),还调用了withPropsDefaultsSetter(Table, tableProps),因此可以在应用中通过Table.setPropsDefaults一次性修改组件的全局默认属性,而不必逐个传参。
基本使用
最直接的用法是把原生表格结构放进var-table。组件会套用--table-row-height(默认46px)的行高、--table-row-padding(默认8px 16px)的单元格内边距、表头下边框与行悬停背景等完整样式:
<template> <var-table> <thead> <tr> <th>姓名</th> <th>数学</th> <th>英语</th> </tr> </thead> <tbody> <tr> <td>耗子君</td> <td>124</td> <td>38</td> </tr> <tr> <td>火猫桑</td> <td>100</td> <td>135</td> </tr> </tbody> </var-table> </template>对应的真实示例页见 example/index.vue。两点值得注意的细节来自 table.less:
.var-table__main上设置了overflow: auto且表格min-width: 100%,所以当列总宽超出容器时,横向滚动条出现在.var-table__main这一层,而不是破坏卡片外框;- 行悬停效果通过
tr:hover { background: var(--table-tbody-tr-hover-background) }实现,带0.25s的背景过渡,td用background: inherit继承行背景,保证悬停色铺满整行。
卡片形态之外:plain、bordered 与 surface
默认形态下,Table是一张 Material 风格的"卡片"。Table.vue中类名的拼装逻辑如下:
[!plain, formatElevation(elevation, 1)] // plain 为真时不渲染 elevation 类 [surfaceLow, n('--surface-low')] [bordered, n('--bordered')] [plain, n('--plain')]也就是说,plain不仅改变背景,还会取消阴影类(测试tests/index.spec.js 中明确断言:设置plain: true后不再包含var-elevation--1)。各形态的视觉效果由 table.less 中的修饰类切换 CSS 变量实现:
var-table--plain:将--table-background置为transparent、border-radius置0,行悬停背景换成更弱的--table-plain-row-hover-background(默认hsla(var(--hsl-on-surface), 0.04));var-table--bordered:给容器加border: var(--table-border);同时--plain--bordered组合会恢复--table-border-radius的圆角;var-table--surface-low:把--table-background与行悬停背景切换为--table-surface-low-background/--table-surface-low-row-hover-background,默认都指向 MD3 的var(--color-surface-container-highest),即"弱背景层级"。
纯表格(plain)
设置plain可以一并去掉卡片阴影、背景色和圆角,适合把表格嵌入已有卡片或列表容器内部:
<template> <var-table plain> <thead> <tr> <th>姓名</th> <th>数学</th> <th>英语</th> </tr> </thead> <tbody> <tr> <td>耗子君</td> <td>124</td> <td>38</td> </tr> <tr> <td>火猫桑</td> <td>100</td> <td>135</td> </tr> </tbody> </var-table> </template>plain + bordered
同时使用plain和bordered,可以用外边框展示更纯粹的表格形态——背景全透明,仅靠--table-border描出轮廓:
<template> <var-table plain bordered> <thead> <tr> <th>姓名</th> <th>数学</th> <th>英语</th> </tr> </thead> <tbody> <tr> <td>耗子君</td> <td>124</td> <td>38</td> </tr> <tr> <td>火猫桑</td> <td>100</td> <td>135</td> </tr> </tbody> </var-table> </template>弱背景色(surface="low")
通过surface="low"使用更接近 MD3 的弱背景层级。源码中surfaceLow是一个计算属性,当且仅当props.surface === 'low'时挂载--surface-low类,因此目前'low'是唯一合法取值(见 props.ts 中的TableSurface = 'low'):
<template> <var-table surface="low"> <thead> <tr> <th>姓名</th> <th>数学</th> <th>英语</th> </tr> </thead> <tbody> <tr> <td>耗子君</td> <td>124</td> <td>38</td> </tr> <tr> <td>火猫桑</td> <td>100</td> <td>135</td> </tr> </tbody> </var-table> </template>三种形态的取舍可以概括为:卡片浮层用默认形态、嵌在既有容器内用plain、需要强轮廓线用plain bordered、需要主题色分层(深色主题下尤其明显)用surface="low"。
尾部插槽:挂载 Pagination 等页面级元素
footer插槽用于在表格主体下方插入内容,最常见的是插入var-pagination实现前端分页。Table.vue中用v-if="$slots.footer"控制.var-table__footer的渲染——没有使用时不产生任何 DOM(测试用例 "table should not render footer without footer slot" 验证了这一点),使用时则自带min-height: var(--table-row-height)与border-top: var(--table-footer-border-top):
<script setup> import { ref } from 'vue' const list = ref(gen(1, 10)) function gen(current, size) { return Array.from({ length: size }).map((_, index) => { const id = (current - 1) * size + index + 1 return { name: `Name ${id}`, math: id, english: id, } }) } function get(current, size) { list.value = gen(current, size) } </script> <template> <var-table> <thead> <tr> <th>姓名</th> <th>数学</th> <th>英语</th> </tr> </thead> <tbody> <tr v-for="l in list" :key="l.name"> <td>{{ l.name }}</td> <td>{{ l.math }}</td> <td>{{ l.english }}</td> </tr> </tbody> <template #footer> <div class="table-example-footer"> <var-pagination :simple="false" :current="1" :total="100" :size-option="[5, 10]" @change="get" /> </div> </template> </var-table> </template> <style> .table-example-footer { display: flex; justify-content: flex-end; align-items: center; height: 60px; padding: 0 16px; } </style>一个容易踩坑的结构细节:默认插槽里的内容最终落在.var-table__table(<table>元素)内部,因此thead/tbody必须直接写在默认插槽中;而footer插槽位于滚动容器之外,放在其中的分页器在纵向滚动时始终可见,不会被表格内容推走。
固定表头:scroller-height 与 sticky 的组合
数据量大的表格通常需要"局部纵向滚动 + 表头吸顶"。Table的做法是把scroller-height作为max-height内联到.var-table__main(该元素本身就是overflow: auto的滚动容器),再由用户给thead加上position: sticky; top: 0。浏览器原生支持在table-header-group上吸附,配合 table.less 中thead { display: table-header-group }的显式声明即可生效:
<script setup> import { ref } from 'vue' const list = ref(gen(1, 10)) function gen(current, size) { return Array.from({ length: size }).map((_, index) => { const id = (current - 1) * size + index + 1 return { name: `Name ${id}`, math: id, english: id, } }) } function get(current, size) { list.value = gen(current, size) } </script> <template> <var-table scroller-height="400px"> <thead style="position: sticky; top: 0"> <tr> <th>姓名</th> <th>数学</th> <th>英语</th> </tr> </thead> <tbody> <tr v-for="l in list" :key="l.name"> <td>{{ l.name }}</td> <td>{{ l.math }}</td> <td>{{ l.english }}</td> </tr> </tbody> <template #footer> <div class="table-example-footer"> <var-pagination :simple="false" :current="1" :total="100" :size-option="[5, 10]" @change="get" /> </div> </template> </var-table> </template> <style> .table-example-footer { display: flex; justify-content: flex-end; align-items: center; height: 60px; padding: 0 16px; } </style>实现原理可以拆解为三步:
scroller-height经toSizeUnit转换后写入.var-table__main的max-height。toSizeUnit的定义在 utils/elements.ts:数字值自动补px单位(如400→400px),字符串则原样透传,所以scroller-height="400px"与scroller-height="400"等价;.var-table__main天生是overflow: auto容器,内容超出max-height即在卡片内部出现纵向滚动条,而不是撑开整个页面;thead声明position: sticky; top: 0后,在滚动容器内滚动时表头行钉在容器顶部。表头行背景为--table-background(非 plain 模式下不透明),滚过的行内容不会从表头下透出。
测试用例tests/index.spec.js 分别用scrollerHeight: '300px'与scrollerHeight: 300两种入参,断言.var-table__main的style中包含max-height: 300px,覆盖了字符串与数字两种类型。
scroller-height是3.2.0版本引入的 Prop,在旧版本中需要用外部容器限制高度来近似实现同样效果。
API 参考
Props
来自 props.ts 与 types/table.d.ts:
| Prop | 描述 | 类型 | 默认值 |
|---|---|---|---|
full-width | table的宽度(包含可滚动部分) | string | number | 100% |
elevation | 海拔高度(阴影等级),可选值为truefalse和0-24的等级 | string | number | boolean | true |
plain | 是否以纯表格形态渲染,不带卡片阴影、背景色和圆角 | boolean | false |
bordered | 是否显示外边框 | boolean | false |
surface | 弱背景色风格,仅支持'low' | 'low' | - |
scroller-height3.2.0 | 滚动容器最大高度,可用于实现纵向局部滚动、固定表头等功能 | string | number | - |
几个与源码对应的行为细节:
full-width最终作为内联width写到.var-table__table上,同样经toSizeUnit处理;默认100%表示表格至少占满容器(less 中还声明了min-width: 100%),传入更大的固定宽度(如full-width="1200")可让窄屏下出现横向滚动;elevation的处理逻辑在 utils/components.ts 的formatElevation:false时返回null(完全无阴影类);true时被Table传入的默认等级1替代,即挂载var-elevation--1;数字/字符串等级则生成var-elevation--N,对应的box-shadow来自 styles/elevation.less 中0到24的全套定义;且plain为真时 elevation 类被整体跳过,阴影自然消失;- 测试用例tests/index.spec.js 对以上行为均有断言:默认渲染包含
var-elevation--1类、elevation: 3切换为var-elevation--3、fullWidth: 200时.var-table__table内联样式为width: 200px。
Slots
| 名称 | 描述 | 参数 |
|---|---|---|
default | Table 的内容,须为合法的表格结构(thead/tbody等) | - |
footer | Table 尾部内容,位于滚动容器之外 | - |
样式变量
以下为组件使用的 CSS 变量,可以通过 StyleProvider 组件进行样式定制。变量默认值集中定义在 table.less 的:root块中:
| 变量名 | 默认值 | 作用位置 |
|---|---|---|
--table-background | #fff | 容器与thead/tbody行背景、footer 背景 |
--table-surface-low-background | var(--color-surface-container-highest) | surface="low"时的表格背景 |
--table-surface-low-row-hover-background | var(--color-surface-container-highest) | surface="low"时的行悬停背景 |
--table-plain-row-hover-background | hsla(var(--hsl-on-surface), 0.04) | plain时的行悬停背景 |
--table-border-radius | 2px | 卡片圆角(plain时为0) |
--table-border | thin solid var(--color-outline) | bordered时的外边框 |
--table-thead-border-bottom | thin solid var(--color-outline) | 表头下边框 |
--table-thead-th-text-color | rgba(0, 0, 0, 0.6) | 表头文字颜色 |
--table-thead-th-text-align | left | 表头对齐方式 |
--table-thead-th-font-size | 14px | 表头字号 |
--table-thead-th-font-weight | 500 | 表头字重 |
--table-thead-tr-border-bottom | thin solid var(--color-outline) | 多行表头中非末行行边框 |
--table-tbody-tr-hover-background | #f5f5f5 | 数据行悬停背景 |
--table-tbody-tr-border-bottom | thin solid var(--color-outline) | 数据行分隔线 |
--table-tbody-td-text-color | #555 | 单元格文字颜色 |
--table-tbody-td-font-size | 14px | 单元格字号 |
--table-tbody-td-text-align | left | 单元格对齐方式 |
--table-row-height | 46px | th/td高度,也是 footer 的min-height |
--table-row-padding | 8px 16px | th/td内边距 |
--table-footer-border-top | thin solid var(--color-outline) | footer 上边框 |
由于这些变量是层层覆盖的设计(例如--plain修饰类只是重写了--table-background和悬停色,并未复制整套规则),定制时遵循"改变量而不覆盖类名"的原则,plain、surface="low"等形态就能自动继承你的定制值。
小结
var-table的设计边界非常清晰:它提供 Material 风格的视觉外壳(卡片阴影、行悬停、分隔线、MD3 弱背景)、可控的滚动容器(scroller-height)和页面级扩展点(footer插槽),而把表格内容组织、数据获取完全留给使用者。当你只需要"一张长得体面的表"时,默认形态 + 样式变量已经足够;当需要固定表头、嵌入分页、与主题色分层联动时,上文给出的sticky表头与surface="low"组合可以直接照搬。更复杂的数据网格场景(列配置驱动、排序、跨行合并等)则建议评估仓库中另外的>赞
- UI组件
- 前端
【免费下载链接】varlet
A Vue3 component library based on Material Design 2 and 3, supporting mobile and desktop.
相关推荐
vxe-table单元格样式定制:从基础样式到条件格式
vxe table单元格样式定制:从基础样式到条件格式 在数据可视化领域,表格的单元格样式定制是提升用户体验和数据可读性的关键环节。无论是突出显示关键指标、区分
前端UI组件rsuite Table 固定表格(Sticky Table)完全指南:将表头与横向滚动条固定在页面指定位置
rsuite Table 固定表格(Sticky Table)完全指南:将表头与横向滚动条固定在页面指定位置 本篇技术指南围绕 rsuite( rsuite t
前端UI组件Ant Design Table 组件 Token 定制指南:基于 ConfigProvider 深度自定义表格样式
Ant Design Table 组件 Token 定制指南:基于 ConfigProvider 深度自定义表格样式 导读 本文围绕 Ant Design 中
前端UI组件设计系统