- 前端
- UI组件
【免费下载链接】table
🤖 Headless UI for building powerful tables & datagrids for TS/JS - React-Table, Vue-Table, Solid-Table, Svelte-Table
本文以@tanstack/react-table的 legacy 兼容层 APIgetCoreRowModel为切入点,讲解该函数在 v8 兼容接口中的存根(stub)定位、在useLegacyTable迁移场景中的真实调用方式,并结合table-core源码剖析 v9 中核心行模型(core row model)的自动创建机制、行模型流水线(pipeline)与缓存策略,帮助你在从 v8 迁移到 v9 的过程中正确理解并移除对getCoreRowModel的依赖。
一、getCoreRowModel 是什么
getCoreRowModel是 React Table 中一个返回行模型工厂函数(RowModelFactory)的 API,完整签名如下:
function getCoreRowModel<TData>(): RowModelFactory<TData>;- 类型参数
TData:TData extends RowData,即表格的行数据类型,通常是一个包含列字段的对象接口。 - 返回值:
RowModelFactory<TData>,其类型定义为:
type RowModelFactory<TData> = (table) => () => RowModel<LegacyFeatures, TData>;也就是说,工厂函数接收一个Table<LegacyFeatures, TData>实例,返回一个“行模型生产函数”,调用该函数即可得到RowModel<LegacyFeatures, TData>(包含rows、flatRows、rowsById三个结构,见下文源码分析)。
在文档中该函数被标记为Deprecated(弃用),官方给出的核心结论是:
The core row model is always created automatically in v9. This is a stub function for v8 API compatibility with
useLegacyTable. It does nothing - the core row model is always available.
这句话包含两层关键信息:
- v9 中核心行模型始终自动创建,你不需要(也不能)通过显式传入
getCoreRowModel()来启用它; - 当前文档中这个
getCoreRowModel是为useLegacyTable提供的 v8 API 兼容存根,函数体本身不做任何事。
二、存根函数的源码真相
文档中“It does nothing”并非泛泛而谈,打开 packages/react-table/src/useLegacyTable.ts 可以看到其真实实现:
/** * @deprecated The core row model is always created automatically in v9. * * This is a stub function for v8 API compatibility with `useLegacyTable`. * It does nothing - the core row model is always available. */ export function getCoreRowModel< TData extends RowData, >(): RowModelFactory<TData> { return (() => () => {}) as unknown as RowModelFactory<TData> }从源码可以看到,getCoreRowModel返回的是一个双重箭头函数(() => () => {}),即“工厂返回一个什么都不做的函数”,并通过类型断言伪装成RowModelFactory<TData>。它真正的用途是作为标记(marker):useLegacyTable通过检查 options 中是否传入了这些get*RowModel存根函数,来决定启用哪些 v9 内部特性。
这一点在 useLegacyTable.ts 的文件注释中有明确说明:
These are stub functions that act as markers for useLegacyTable to know which row models to enable. They don't actually do anything - the real implementation is handled by useLegacyTable internally.
值得注意的是,与其他get*RowModel存根(如getFilteredRowModel、getSortedRowModel)不同——那些存根会触发useLegacyTable在内部启用对应的特性(例如传入getFilteredRowModel会激活filteredRowModel特性)——getCoreRowModel在useLegacyTable的选项解构中被直接丢弃:
const { getCoreRowModel: _getCoreRowModel, // 被解构并忽略,以下划线命名提示不参与逻辑 getFilteredRowModel, getSortedRowModel, // ... } = options因为核心行模型在 v9 中永远存在,无需任何条件启用,所以该存根连“标记”作用都不需要承担。
三、使用场景:useLegacyTable 迁移兼容层
getCoreRowModel唯一有实际意义的场景,是配合useLegacyTable钩子进行 v8 → v9 的增量迁移。useLegacyTable接受 v8 风格的表选项,但在底层运行 v9 引擎,适合“有存量 v8 代码库、希望逐表渐进迁移”的团队。
在 docs/framework/react/guide/use-legacy-table.md 的示例中,getCoreRowModel与其他行模型函数一起从@tanstack/react-table/legacy导入并传给useLegacyTable:
import { getCoreRowModel, getFilteredRowModel, getPaginationRowModel, getSortedRowModel, legacyCreateColumnHelper, useLegacyTable, } from '@tanstack/react-table/legacy' const table = useLegacyTable({ columns, data, getCoreRowModel: getCoreRowModel(), getFilteredRowModel: getFilteredRowModel(), getSortedRowModel: getSortedRowModel(), getPaginationRowModel: getPaginationRowModel(), state: { sorting, columnFilters, pagination }, onSortingChange: setSorting, onColumnFiltersChange: setColumnFilters, onPaginationChange: setPagination, })配套使用的 v8 风格行模型函数清单(全部从@tanstack/react-table/legacy导入):
| 函数 | 作用 |
|---|---|
getCoreRowModel() | 核心行模型(v9 自动创建,此处仅为兼容) |
getFilteredRowModel() | 启用过滤后的行模型 |
getSortedRowModel() | 启用排序后的行模型 |
getPaginationRowModel() | 启用分页后的行模型 |
getExpandedRowModel() | 启用展开后的行模型 |
getGroupedRowModel() | 启用分组后的行模型 |
getFacetedRowModel() | 启用 facet 行模型 |
getFacetedMinMaxValues() | 启用 facet 列 min/max 值 |
getFacetedUniqueValues() | 启用 facet 列唯一值 |
使用 useLegacyTable 的前提
- 你拥有存量 v8 代码库,需要升级依赖;
- 希望一次迁移一张表,逐步完成;
- 暂时没有时间做完整迁移,但需要 v9 兼容能力。
使用注意与限制
- 已弃用:
useLegacyTable是临时迁移工具,未来大版本会被移除; - 包体积大:它默认启用全部特性,无法享受 v9 按需 tree-shaking 的收益;
- 全量订阅:它始终订阅整个表格状态,组件会在每次状态变化时重渲染,无法获得 v9
useTable细粒度选择器的渲染优化; - 不支持
createTableHook:无法与其组合复用表格配置。
四、v9 中核心行模型为何“始终可用”
v9 中核心行模型的自动创建逻辑位于table-core包。当useLegacyTable内部调用useTable时,核心行模型由两个部分协同提供。
4.1 核心行模型工厂:createCoreRowModel
packages/table-core/src/core/row-models/createCoreRowModel.ts 定义了 memoized 的核心行模型工厂:
export function createCoreRowModel<TFeatures, TData>(): ( table: Table_Internal<TFeatures, TData>, ) => () => RowModel<TFeatures, TData> { return (table) => { return tableMemo({ feature: 'coreRowModelsFeature', table, fnName: 'table.getCoreRowModel', memoDeps: () => [table.options.data], fn: () => _createCoreRowModel(table, table.options.data), onAfterUpdate: skipFirstRun(() => { table_autoResetExpanded(table) table_autoResetPageIndex(table) table_autoResetSorting(table) table_autoResetCellSelection(table) }), }) } }关键机制:
- 记忆化:通过
tableMemo缓存行模型,memoDeps仅依赖table.options.data,即只有数据数组引用变化时才重建核心行模型; - 自动重置联动:数据更新后,
onAfterUpdate会依次触发展开、页码、排序、单元格选择等状态的自动重置,这正是“数据一变,派生状态复位”的底层来源。
核心行模型的实际构建发生在_createCoreRowModel(同文件 L90-L110),产出三种结构:
const rowModel: RowModel<TFeatures, TData> = { rows: [], // 顶层行数组(含父子层级) flatRows: [], // 扁平化全量行数组 rowsById: makeObjectMap(), // 以行 ID 为键的索引表 }行对象的构建由accessRows递归完成:通过table.getRowId生成行 ID、按getSubRows递归嵌套子行,同时维护flatRows与rowsById,为后续排序、过滤、分组、展开、分页各阶段提供统一的输入。
4.2 获取器与缓存:coreRowModelsFeature
packages/table-core/src/core/row-models/coreRowModelsFeature.ts 将table.getCoreRowModel等 13 个 API 挂载到表格实例上。其中table_getCoreRowModel的实现(coreRowModelsFeature.utils.ts L18-L29)展示了“自动创建”的完整逻辑:
export function table_getCoreRowModel<TFeatures, TData>( table: Table_Internal<TFeatures, TData>, ): RowModel<TFeatures, TData> { if (!table._rowModels.coreRowModel) { table._rowModels.coreRowModel = table.options.features.coreRowModel?.(table) ?? createCoreRowModel<TFeatures, TData>()(table) } return table._rowModels.coreRowModel() }解读:
- 每个表格的
_rowModels.coreRowModel工厂只创建一次(懒加载); - 优先使用
features选项中用户自定义的coreRowModel槽位; - 若未提供,则自动回退到内置的
createCoreRowModel()——这就是“core row model 始终自动创建”的直接源码依据。
五、核心行模型在行模型流水线中的位置
v9 内置的行模型处理是一条流水线(pipeline),各阶段的读取顺序与别名关系在 coreRowModelsFeature.utils.ts 中清晰可见:
core -> filtering -> grouping -> sorting -> expanding -> pagination各阶段获取器之间的关系如下(源码为证):
| 获取器 | 行为 | 依据 |
|---|---|---|
table.getPreFilteredRowModel() | 别名为getCoreRowModel() | L42-L47 |
table.getPreGroupedRowModel() | 别名为getFilteredRowModel() | L87-L92 |
table.getPreSortedRowModel() | 别名为getGroupedRowModel() | L131-L136 |
table.getPreExpandedRowModel() | 别名为getSortedRowModel() | L176-L181 |
table.getPrePaginatedRowModel() | 别名为getExpandedRowModel() | L221-L226 |
table.getRowModel() | 别名为getPaginatedRowModel()(流水线终点) | L267-L272 |
当某个阶段未注册对应特性时(例如没有启用过滤),中间获取器会原样返回上一阶段的行模型。这一点也解释了为什么 v8 中需要显式传入getCoreRowModel():v8 的每一级行模型都靠用户手动装配,而 v9 把最基础的 core 阶段固化为内置默认值,只有可选的过滤/排序/分页等阶段才需要按需注册。
六、迁移到 v9:如何移除 getCoreRowModel
按 docs/framework/react/guide/migrating.md 的迁移对照表,getCoreRowModel()对应的 v9 处理是:
| v8 | v9 |
|---|---|
getCoreRowModel() | (自动) —— 无需提供,始终内置 |
迁移路径(完整步骤见 use-legacy-table.md 与 migrating.md):
- 将
useLegacyTable替换为useTable; - 用
tableFeatures()显式声明所需的特性,例如:
import { tableFeatures } from '@tanstack/react-table' import { createFilteredRowModel, createSortedRowModel, createPaginatedRowModel, columnFilteringFeature, rowSortingFeature, rowPaginationFeature, filterFns, sortFns, } from '@tanstack/table-core' const features = tableFeatures({ columnFilteringFeature, rowSortingFeature, rowPaginationFeature, filteredRowModel: createFilteredRowModel(), sortedRowModel: createSortedRowModel(), paginatedRowModel: createPaginatedRowModel(), filterFns, sortFns, }) const table = useTable({ features, columns, data, })- 将
get*RowModel()选项转换为rowModels特性槽位(如filteredRowModel、sortedRowModel、paginatedRowModel),并删除getCoreRowModel: getCoreRowModel()这一行——它在新 API 中完全不需要; - 把
Legacy*类型更新为标准 v9 类型(LegacyColumnDef→ColumnDef、LegacyRow→Row等)。
迁移后还能获得 v9 的额外收益:按需引入特性、tree-shaking 减小包体积、通过table.Subscribe实现细粒度重渲染、通过table.state直接访问状态。
七、测试证据:核心行模型的行为契约
table-core的单元测试 packages/table-core/tests/unit/core/row-models/coreRowModelsFeature.utils.test.ts 用断言固化了 core 行模型的行为:
- 未启用任何可选特性时,整条流水线各阶段(
getPreFilteredRowModel、getFilteredRowModel、getSortedRowModel、getExpandedRowModel、getPaginatedRowModel、getRowModel等)返回的都是同一个coreRowModel实例(L47-L67); - 核心行模型工厂在表格生命周期内只创建一次:即使连续多次调用
table.getCoreRowModel(),工厂函数也仅被调用一次(L71-L88)。
这两条断言分别印证了前文提到的“流水线默认直通”与“工厂懒加载单例”机制,可以作为你理解或验证核心行模型行为的参考。
八、总结
getCoreRowModel在 React Table 的 legacy 兼容层中是一个纯存根函数,函数体不执行任何逻辑,仅用于 v8 风格 API 的代码兼容;- v9 中核心行模型由
table-core的createCoreRowModel+coreRowModelsFeature自动创建、懒加载并记忆化,无需也无法通过该函数手动启用; - 如果你在维护 v8 迁移中的代码,可以在
useLegacyTable中保留getCoreRowModel: getCoreRowModel()保持兼容,但应将其视为临时代码,并尽快按迁移指南删除,改用 v9 的useTable+tableFeatures()组合。
- 前端
- UI组件
【免费下载链接】table
🤖 Headless UI for building powerful tables & datagrids for TS/JS - React-Table, Vue-Table, Solid-Table, Svelte-Table
相关推荐
深入 TanStack Table 核心行模型:CachedRowModel_Core 接口与 getCoreRowModel 的实现原理
深入 TanStack Table 核心行模型:CachedRowModel_Core 接口与 getCoreRowModel 的实现原理 导读 CachedR
前端UI组件Granite-3.0-3B-A800M-Base全面解析:12种语言支持的革命性文本生成模型
Granite 3.0 3B A800M Base全面解析:12种语言支持的革命性文本生成模型 Granite 3.0 3B A800M Base是一款由IBM
前端UI组件@tanstack/octane-table API 参考:TanStack Table v9 的 Octane 适配层接口与函数全解
@tanstack/octane table API 参考:TanStack Table v9 的 Octane 适配层接口与函数全解 本文是 @tanstac
前端UI组件
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考