范围:本导览只做
03-components/08-table这 7 篇之间的跨篇路由——你手上这条「表格类症状」该翻组件侧哪一篇,还是该直接跳到基座侧。渲染内核(测量行、固定列 offset、克隆表头、单元格拆分、滚动同步)、table-core的派生顺序与状态袋形态、虚拟化两套引擎的窗口数学,分别在I05、I06、I08已经论证完,本篇一律不复述;主题 Token 与样式表身份归A03、A04,props 默认值与注入链归A05、A06,受控合并语义归I13,语言包供给侧归I11。
这一族 7 篇里只有 4 个「把一份数组渲染成结构化条目」的实现,另外 2 篇是虚拟化容器、1 篇是类横写。它们的差别不在功能多少,而在三个真源各自握在哪一层:列定义的真源、状态袋的真源、可见窗口的真源。table 把列编译成数组后交给内核,本体一行 DOM 都不产出(C08-01 论点 1);data-table 把列的本体做成 VNode,columns prop 被降级成「造 VNode 的原料」(C08-02 论点 3);tree-table 的列只能由默认插槽的子节点产出(C08-00 论点 2);list 干脆不认识「行、列、单元格」这三个概念(C08-05 §一)。先定住这三个真源,后面所有「传了不生效」的症状都能一眼归层。
状态流向在本族是三条互不兼容的路线,不是一条路线的三种粗细。table 是单向闭环:props 进、五段编译链在内核渲染期回调、一次 onChange 出,而载荷里的数据是重跑管道算出来的、不是复用渲染用的那份(C08-01 论点 3)。data-table 是反向的:12 个独立 ref 组成状态袋、39 个具名事件各自写回,翻页一次对外发四个信号(C08-00 §三、I06 论点 5、C08-00 §五)。tree-table 最特殊:勾选的真源在调用方,展开才是半受控,于是同一张表里出现两种心智模型(C08-03 论点 2、I06 论点 6)。三者的事件命名风格也不统一,只能当成三套契约对待,这一点是迁移成本而不是 bug(C08-00 §三)。
分层上有三处断裂,读者必须记住它们决定了「改动的爆炸半径」:渲染内核只有一条入边(来自 table),table-core 有两条入边但深度完全不同,data-table/composables/ 是一个只被自己测试引用的悬空框(C08-00 §三)。直接含义有两条:任何「修表格渲染 bug」的默认落点只在 table/,另外两张表是自己手写 DOM 的(C08-00 论点 1);而任何「修 table-core 排序」的落点大概率在没人调用的那一份上(I06 论点 3、I06 坑 1)。
因此本篇最重要的用法是同一症状的两层归位。最典型的是「列没写 key」:在组件侧它表现为排序与筛选状态在增删列后串到别的列(C08-01 坑 1),在内核侧它表现为测量宽度串到另一列(I05 坑 3)——同一个键,两个后果,两条修复路线,谁都不覆盖谁。第二个例子是 lazy:data-table 的 lazy 是关掉本地管道的合取守卫(C08-00 坑 3),tree-table 的 lazy 是管道顶端一次 return(C08-00 §三);「服务端分页不生效」这句话在两张表里是两件事。
虚拟化在本族只有一条真实通路,它同时改写了数据管道与 DOM 选择器(C08-00 论点 4)。两份虚拟化拷贝里,库内实引擎是被 data-table 直接导入的那一份,另一份是库内零消费者的公开件(C08-07 论点 3、C08-06 §一);它们的窗口数学、高度策略与 overscan 归 I08,本篇不重列。选型判据只有一条:行高能不能保证恒等——能,用声明式引擎;不能,换带测量回路的那一份(I08 论点 3)。
派工提示里的「ProTable / 可编辑表格」这一层在本族没有对应篇目,7 篇里没有任何一篇出现这个组件,本篇不虚构它。真实存在的「可编辑」通道只有 data-table 的列级编辑插槽(children.editor,编辑态与非编辑态在同一个渲染函数里分流,C08-00 论点 3),而另一个常被拿来对照的行内编辑组件属于 06 类、并且不被本仓任何组件依赖(C06-05 §一)。想给表格配一套「声明式可编辑表格」的人,正确的读序是 C08-02、C08-00 论点 3、C06-05 §一,不是去找一个不存在的上层组件。
| 要解决的问题 | 选它 | 而不是另一个 | 判据 |
|---|---|---|---|
| 大数量级行、要在表格里做虚拟滚动 | data-table(底层是声明式引擎那一份) | table | 本族只有一条真实虚拟化通路,table 侧只留 components.body 一条接缝且没有开箱实现,接缝形态归 I05 论点 10(C08-00 论点 4) |
| 树形层级 + 父子半选 | tree-table | data-table | 半选只在 tree-table 的向上归算里诞生、data-table 结构性缺失,table 虽有但是从树组件借来的算法(C08-03 论点 2、C08-00 §三) |
| 列想用模板子组件写 | table 的列子组件(数组 prop 的糖,渲染前一次转换) | data-table 的列子组件 | 后者既没进全局标签声明也没有包导出,写了不渲染任何列(C08-00 论点 2、C08-02 坑 6) |
| 多级表头(列分组) | table | data-table | 分组收集端恒空导致表头分组分支永不进入,这条链路上多级表头不可用(C08-02 坑 6);内核侧 columns 到单元格矩阵是两步、且两步用的是两份数据(I05 论点 6) |
| 一次交互只想接一个回调(服务端整包重取) | table 的 onChange | data-table 的具名事件族 | 前者四段状态一次上报但为了载荷把管道重跑一遍,后者翻页一次发四个事件、第四个还排在 nextTick(C08-01 论点 3、C08-00 §三) |
| 要加载遮罩、分页切片、栅格多列三件套 | list | C08-06 / C08-07 虚拟化容器 | List 的四套状态各有落点且分页有两套互不相干的控制路径(C08-05 论点 3);条目总量大到要窗口化时改走虚拟化容器,但那两条路不再提供加载遮罩与分页切片 |
| RTL、全局语言、全局空态必须生效 | table | 另外三张容器与表格 | 只有 table 解构了 direction 与 renderEmpty 并接了语言包;data-table 只接 prefixCls(C08-00 坑 7) |
| 冻结列与固定表头该用哪张表 | 三张都能,按其它判据选表 | 以「能否冻结」当判据 | 三条实现互不相干,只有 table 走测量行与左右 offset 累加,另外两张留在自己目录里实现,tree-table 那份甚至绕开响应式直接读写全局 DOM(C08-00 §三、I05 论点 3、C08-03 论点 6) |
| 虚拟化容器在两份拷贝之间怎么选 | 公开件那一份对外使用 | 实引擎那一份 | 两者是文件级拷贝不是分层,实引擎的唯一组件级消费者是 data-table;对外应依赖公开件(C08-06 §一、C08-07 论点 3、I08 论点 1) |
| 条目高度不可预知时选哪个引擎 | 带测量回路的内部基座 | 声明式 itemSize 那两份 | 声明式引擎全程不测单项高度、窗口数学是纯乘法,不符即漂移(I08 论点 3、C08-07 论点 2) |
族谱一:传了不生效、写了不上身(契约面)
class、style、id,原生事件也不触发 → C08-02 坑 1;同款两处还有 C08-06 坑 2、C08-07 坑 1 与 坑 2,机制侧口径归 A06 坑 2。data-table 传 tableProps 反而把内部基座类名拆掉 → C08-02 坑 2;同族的「展开顺序」判读要注意 on 事件键不进覆盖语义,见 A06。undefined 乘负一塌成 NaN 并一路传进比较器(I06 坑 2);给列传 sortOrder: undefined 把列永久钉成受控则是另一条判据(C08-01 坑 2)。show-gridlines、striped-rows、row-hover 三个开关永远无效 → C08-02 坑 4,且它前面还压着一层「样式表整条错位、改了也看不见」(C08-02 坑 3)。List 传 extra 什么都不出现、loading 传对象不带 spinning 就没遮罩 → C08-05 坑 1、C08-05 坑 3;水平布局下条目 extra 只留第一个节点是第三条(C08-05 坑 4)。prefixCls 只改到根节点,条目类名留在旧前缀 → C08-05 坑 2;同类「喂给样式钩子的前缀不是本组件的、于是同一份样式注册成两张表」的判形见 A04 论点 1。C08-02 坑 3 与 C08-05 坑 8 是同族的两面:前者是规则与类名互不相认,后者当时是四条选择器(.xy-list-spin、.xy-list-loading .xy-list-spin-nested-loading、窄屏 .xy-list-action、等不到根类名的 .xy-list-empty footer 边框)全部挂在代码从不生成的类名上。C08-05 那四条已在 2026-09-29 接回真实类名,判别方法只有一个:把「组件生成的类名集合」与「样式表引用的类名集合」相减,两个方向都要看。修完的代价是加载态高度与窄屏边距从此真的生效,所以这类改动不再属于「删了也不会变」的安全操作,C08-02 坑 3 已在 2026-09-30 修完:155 处 &__ 与 40 处根级 &-- 一次对齐到 DOM 的单横线命名(公开类名不变),验收读的是注入到 document 的真实 CSS,三条判据在基线全红;同篇 坑 4 同日一并修掉:gridlines/striped/hoverable 缺的是类名生产者(根类名唯一来源 DataTable.tsx:2299 的 dataP 里没这三个键),补上之后 12+3+2 条规则才有宿主。这条与坑 3 是两个独立断点——样式表对齐了类名不等于开关生效,两端要分别相减。ConfigProvider 的 direction 与全局文案对某张表零效果 → C08-00 坑 7,供给侧整体缺失面在 I11。selectedRowKeys 只给值不给回写,勾选点了没反应 → C08-01 坑 4;tree-table 侧对应的是「本地态吞掉受控语义」(C08-03 坑 6);合并语义的机制归 I13。total 时翻页像失效 → C08-05 坑 5(total 缺省被 dataSource.length 顶替,越界页码被夹回)。族谱二:数据和状态不对(管道面)
C08-00 坑 3;新增派生阶段忘复制那条门控是它的镜像症状(I06 坑 8)。lazy 模式下「服务端分页不生效」长得各不相同 → data-table 的 lazy 是关掉本地管道的合取守卫(C08-00 坑 3),tree-table 的 lazy 是管道顶端一次 return(C08-00 §三);两条判据不能互相套用。C08-03 坑 2,同一条也在 I06 坑 5 登记;两处结论一致,行号定位以 C08-03 坑 2 为准。I06 坑 6;tree-table 没有表头全选框,勾选只能逐行发起(C08-03 §一)。C08-03 坑 3、C08-03 坑 4。preserveSelectedRowKeys 打开后行数组里混进 undefined → C08-01 坑 5;跨页能解析到记录的前提见 C08-01 坑 9。@change 在大表上把管道跑两遍、排序响应翻倍 → C08-00 坑 6(triggerOnChange 为组装 currentDataSource 重跑排序与过滤)。data-table 的非受控过滤不成立、双向绑定的多列排序元数据静默失同步 → I06 坑 7、I06 坑 3。族谱三:DOM、类名与结构(渲染面)
I05 坑 1;表格在隐藏容器里首次挂载,固定列叠在一起 → I05 坑 2。C08-00 坑 4;根因是同一选择器被硬编码两遍、没有共同常量可单点改(C08-00 坑 4 定位段)。data-xy-scrollable 永远渲染不出来 → I06 坑 4;-fix-sticky 与 -cell-fix-left-all 是双向都断的类名契约(I05 坑 5)。I05 坑 7、I05 坑 8。tree-table 的冻结列偏移互相污染 → C08-03 坑 5,因为它读的是全局 document 而不是本表根节点。data,一点展开就全开 → C08-03 坑 1;这是「节点三段式」约定被违反的直接后果(C08-03 论点 3)。族谱四:虚拟化(窗口面)
I08 坑 3;virtual-scroller 那一份还额外缺一个数值兜底(C08-07 坑 3),itemSize 缺省为 0 时窗口数学直接塌缩(C08-06 坑 3)。C08-06 坑 1,该篇已核实同款存在于另一份拷贝;scrollToIndex 越界与未挂载全静默 → I08 坑 4、C08-07 坑 4。I08 坑 5;内部基座那一份的两个对应症状是键不唯一与条目必须单根(I08 坑 1、I08 坑 2)。C08-06 坑 4。numToleratedItems 当固定 prop 传却半失效 → C08-06 坑 5。I08 坑 6、C08-07 坑 5,改前先对两份文件跑差异。族谱五:标签、导出与「改了个寂寞」(交付面)
<xy-row> 在运行时到底是哪个组件 → C08-00 坑 5;登记粒度不等(运行时六个标签、类型面只有一条)见 C08-00 论点 5。tree-table 用库里现成的列组件,表头空白且无报错 → C08-00 坑 1;本篇第五节有一条现 grep 的仲裁。data-table/composables/ 里改 bug,测试全绿、行为不变 → C08-02 坑 5、I06 坑 1;为什么它比线上版本更正确却没接线,见 I06 §六。table-core 的 flat 分支去修 data-table → I06 坑 1;只有 tree 侧那一份会传导到组件。import type 拿到的列类型永远是 table 的那一份 → C08-00 论点 5;这条归属是怎么被钉住的,机制在 A02 论点 6,想顺手加一条锁会被 A02 坑 7 反噬。name 或目录名后包根导出面漂移 → C08-00 坑 1 末段,交付链的通用条目见 A02 坑 3、A02 坑 6。C08-07 坑 6(悬空类型让 data-table 的列类型声明失去约束)。I06 论点 9(要动的「7 + 2」处清单,其中两处是生成物与快照),再读 C08-00 论点 1(三张表零共享,新增能力要落几个目录)与 C08-02 论点 1(同一逻辑的第二份拷贝已经存在过一次)。data-table 的排序、过滤或分页行为:落点是 DataTable.tsx 的内联函数,不是 table-core 的 flat 分支也不是 composables/,顺序读 I06 坑 1、I06 坑 2、C08-02 坑 5、C08-02 论点 4。C08-00 论点 2、C08-02 论点 3、I06 论点 1;内核侧只需 columns: any[] 与列属性访问器这一条判据在 I06 §六。C08-00 论点 4(这条通路同时改写了数据管道与 DOM 选择器)、I08 论点 3 与 I08 论点 6、C08-07 §六(换引擎的真实成本在插槽契约形状,不在窗口数学)。C08-00 坑 4(两处硬编码选择器)、C08-02 论点 2(样式表与 DOM 两套命名从不错位)、I05 §六(类名是跨层契约且没有编译期保护,断链实例是 I05 坑 5)。tree-table 的勾选、展开或键盘导航:读 C08-03 论点 1(可见行由渲染递归产生,没有可见行数组)、C08-03 论点 2、C08-03 论点 5、I06 论点 6;接虚拟滚动会首先推翻「折叠子树不存在」这个前提(C08-03 §六)。C08-00 §三(一次 change 对三十九个事件,命名风格三份)、C08-01 §六(源码里作者自陈的受控寄生与计划)、I06 §六(事件名 value-change 与 change 的漂移就是上一次没接线的直接负债)。C08-00 坑 7,再回 A05(注入链与兜底写法)与 A06 坑 5(给需要「未传判定」的键写默认值,同时打断受控判定与注入);data-table 只接 prefixCls 这一事实本身在 C08-00 §五。table 的列编译链或它与内核的握手:读 C08-01 论点 1、C08-01 坑 6(顺序的隐式前置条件)、I05 论点 6、I05 论点 10;「门禁字符串必须两边一致、编译发生在内核渲染期」这笔代价记在 C08-01 §六,反向依赖那条边在 I05 §一 被明确标成不应存在。冲突 1:tree-table 的列到底能不能从 columns prop 进。 C08-00 §二 写的是「它的列定义只能从 columns prop 进(treeTableTypes.ts:162)」,而同篇 论点 2 写的是「treeTableProps() 里根本没有 columns 这个 prop,列只能由插槽子节点提供」。两处口径互斥,现查后以 C08-00 论点 2 为准:
grep -n -e "^export const treeTableProps" -e "^export const treeTableRowProps" -e "columns:" \
packages/xiaoye-ui/src/tree-table/treeTableTypes.ts
# 95: treeTableProps;154: treeTableRowProps;162 的 columns 落在后者体内
grep -n "props.columns" packages/xiaoye-ui/src/tree-table/TreeTable.tsx # 0 命中packages/xiaoye-ui/src/tree-table/treeTableTypes.ts:162 那个 columns 属于行级 props 工厂(同文件 :154),主组件工厂 :95 里没有它,主实现也不读 props.columns。C08-03 §二 从收集端印证同一结论(TreeTable.tsx:68 的收集器加 :98 到 :101 的兜底),因此按 C08-00 §二 那句话去改代码会直接改错方向。
冲突 2:column、columngroup、row 是不是 DataTable 与 TreeTable 的共用层。 实测它们只存在于 data-table/ 目录内、其中只有 column 被生产代码引用,结论以 C08-02 §一 为准——不是共用层:
grep -rn -e data-table -e column/ -e columngroup -e row/Row \
packages/xiaoye-ui/src/tree-table --include='*.ts' --include='*.tsx' | grep -v __tests__ # 0 命中tree-table 的非测试源码对这三件零引用,它们不是共用层;tree-table 之所以看起来「也用列组件」,是因为它的收集器按字符串 'Column' 匹配,而库里没有任何组件的 name 是它(C08-00 论点 2)。这条差别有实用后果:给 data-table 补列组件相关改动不会传导到 tree-table,反之也一样,别按「共用层」的心智去评估回归面。
登记漂移已清(2026-10-02):本类各篇「读者坐标」的分母已按目录内实际篇数(7)重排,data-view 移除后的空号保留不补位。