ag-grid分组(Grouping)还是树形数据(Tree Data),到底怎么选?

373 阅读5分钟

引言(Introduction)

在实际开发中,我们常常困惑为什么设置了分组或者树,但是却不生效。那可能是因为我们搞混了分组和树的配置。在 ag-Grid 中,虽然分组和树的表现形式非常相似,甚至有一些属性也是通用的,但是分组(Grouping)树形结构(Tree Data) 是两种完全不同的数据组织方式,它们的设计目标、使用场景和底层实现都有显著差异。以下从5个维度详细解析二者的区别,并解释为什么 ag-Grid 需要分别实现这两个模块。

提示:本文使用的ag-grid版本是v33,请确保在相应版本下应用代码示例。


一、TreeData 和 Grouping 的相同点

1. 都是用于展示层级数据

无论是 TreeData 还是 Grouping,它们都用于将数据以某种层级或分组的方式进行展示。TreeData 是通过明确的父子关系来构建树形结构,而 Grouping 则是通过列分组来展示数据的类别。

2. 都允许展开和折叠节点或组

在这两种功能中,用户都可以通过展开和折叠节点或分组来控制数据的展示。无论是树形结构中的父节点,还是分组后的数据项,用户都可以展开以查看子项,或者折叠以减少数据的显示量。

  • TreeData:父节点可以展开或折叠以显示其子节点。
  • Grouping:分组行可以展开或折叠,以查看该组中的数据行。

3. 都支持多层级结构

虽然实现方式不同,但两者都可以支持多层级的数据结构:

  • TreeData 支持多层嵌套的子节点,可以创建多层级的树形数据。
  • Grouping 支持通过多个字段进行分组,可以创建多层次的分组结构。

4. 都能够支持聚合和汇总操作

在某些场景下,TreeData 和 Grouping 都可以配合聚合操作使用:

  • TreeData:每个节点可以计算其子节点的聚合信息,如总和、平均值等。
  • Grouping:分组后的数据可以应用汇总函数,如求和、计数、最大值等,用于显示每个分组的数据汇总。

二、核心概念对比

特性分组(Grouping)树形结构(Tree Data)
数据结构来源根据列值动态生成的层次结构数据本身固有的父子层次关系
数据关系临时分组关系(可随时改变分组依据)固定层级关系(数据自带父子连接)
典型场景销售数据按「地区+产品」动态分组分析导航菜单、组织架构等固有树形结构展示
交互特征允许用户拖动列头动态分组固定结构,只能展开/折叠节点

三、技术实现差异

1. 数据组织方式

// 分组数据,分组对数据没有强制要求
const rowData = [
  {
    id: 1,
    name: 'Erica',
    jobTitle: 'CEO',
    
  },
  {
    id: 2,
    name: 'Malcolm',
    jobTitle: 'VP',
  },
  {
    id: 3,
    name: 'Lisa',
    jobTitle: 'Manager',
  },
  {
    id: 4,
    name: 'Robert',
    jobTitle: 'Manager',
  },
];

// 树形数据结构要求(固定父子关系)path表示层级
const rowData = [
  {
    id: 1,
    name: 'Erica',
    jobTitle: 'CEO',
    path: ['1'],
  },
  {
    id: 2,
    name: 'Malcolm',
    jobTitle: 'VP',
    path: ['1', '2']
  },
  {
    id: 3,
    name: 'Lisa',
    jobTitle: 'Manager',
    path: ['1', '2', '3']
  },
  {
    id: 4,
    name: 'Robert',
    jobTitle: 'Manager',
    path: ['1', '2', '4']
  },
];

2. 核心配置对比

import { RowGroupingModule } from 'ag-grid-enterprise';
ModuleRegistry.registerModules([
  RowGroupingModule,
]);
// 分组配置,在需要参与分组的列定义中声明 rowGroup: true,可以在多个列中配置
const gridOptions = {
  columnDefs: [{ field: 'name', rowGroup: true }, { field: 'jobTitle' }],
  autoGroupColumnDef: { /* 树形列配置 */ }
};

// 树形配置
import { TreeDataModule } from 'ag-grid-enterprise';
ModuleRegistry.registerModules([
  TreeDataModule,
]);
const gridOptions = {
  treeData: true, // 必须显式开启
  getDataPath: data => data.path, // 必须定义路径
  autoGroupColumnDef: { /* 树形列配置 */ }
};

3. 节点关系管理

  • 分组:通过 在列声明中设定需要参与分组的数据
  • 树形:通过 getDataPathchildren 字段硬编码层级关系

四、功能特性差异

功能分组树形结构
动态层级变化✅ 支持随时增减分组层级❌ 结构固定
节点路径查询❌ 不需要路径✅ 通过 getDataPath 获取完整路径
数据更新方式自动重组(依赖原始数据变化)需主动维护数据中的父子关系
排序/过滤影响改变分组结构保持树形结构不变

五、为什么需要分别实现?

1. 数据本质不同

  • 分组对平面数据的动态重组
    适用于分析场景,如:[销售记录] => 按地区+产品分组
  • 树形对固有层级数据的直接映射
    适用于展示场景,如:文件系统、菜单权限树

2. 性能优化方向不同

  • 分组 需要优化动态计算(如快速重组、聚合计算)
  • 树形 需要优化静态遍历(如快速查找子节点、路径压缩)

3. API 设计需求不同

  • 分组 需要暴露分组控制 API(如 setRowGroupColumns()
  • 树形 需要节点操作 API(如 getNodePath()setExpanded()

4. 交互模式差异

  • 分组允许用户 动态改变层级结构
  • 树形要求用户 保持固有父子关系

总结:设计哲学差异

维度分组树形
数据关系动态推导(Derived)静态声明(Declared)
用户意图「我想看看数据如何分组」「我想浏览固有的层级结构」
底层实现基于列值的哈希映射基于指针的树遍历
扩展性适合与聚合计算结合适合与层级操作结合

通过这种分离设计,ag-Grid 可以更高效地处理两种截然不同的数据场景,同时保持核心引擎的简洁性。