使用 @visx/hierarchy 在 React 中可视化树形与层次数据:五大布局组件实战指南
2026/9/20 23:25:35 网站建设 项目流程

使用 @visx/hierarchy 在 React 中可视化树形与层次数据:五大布局组件实战指南

【免费下载链接】visx🐯 visx | visualization components项目地址: https://gitcode.com/gh_mirrors/vi/visx

导读

@visx/hierarchy是 visx 体系中专门面向**层次数据(hierarchical data)**的可视化子包,为 React 提供了一组开箱即用的布局组件,整体设计与d3-hierarchy保持一一对应。本指南将以 packages/visx-hierarchy/Readme.md 为核心骨架,结合仓库源码深入讲解:如何用hierarchy()构造 d3 格式的根节点、五种布局(Tree / Cluster / Pack / Partition / Treemap)各自的适用场景与完整 props,以及如何通过渲染回调(render prop)和自定义节点/连线组件实现完全可控的层次可视化。读完本文,你可以直接在自己的 React 项目中搭建树图、圈图、旭日图和矩形树图。

为什么需要层次数据组件

许多真实数据集天然具有层级结构:文件系统目录、公司组织架构、商品类目树、基因谱系、词云词频嵌套……在 React 生态中处理这类数据,通常面临两个问题:

  1. 布局算法复杂:树、集群、打包、分区、矩形树都有各自的几何布局规则,手写成本高;
  2. 与渲染解耦困难:布局结果需要转成 SVG 元素,并且要支持样式定制。

@visx/hierarchy的解决方案是:d3-hierarchy完成布局计算,用 React 组件负责渲染。所有组件接收同一个d3-hierarchy定义的根节点(root node)作为输入,因此你在不同布局之间切换时,数据层完全无需改动。

从 src/index.ts 可以看到,包对外同时导出五类能力:

  • 五种布局组件TreeClusterPackPartitionTreemap
  • 三个默认渲染组件HierarchyDefaultLinkHierarchyDefaultNodeHierarchyDefaultRectNode
  • d3-hierarchy 的布局与工具函数再导出hierarchystratify、以及treemapSquarifytreemapBinarytreemapResquarifytreemapDicetreemapSlicetreemapSliceDice六种 treemap 平铺算法;
  • 完整的类型再导出(见 src/types.ts):HierarchyNodeHierarchyPointNodeHierarchyCircularNodeHierarchyRectangularNodeHierarchyPointLinkHierarchyCircularLinkHierarchyRectangularLinkHierarchyLink以及自定义平铺方法类型TileMethod<Datum>
// equivalent to `import { hierarchy } from 'd3-hierarchy';` import { hierarchy } from '@visx/hierarchy';

安装与依赖

npm install --save @visx/hierarchy

根据 package.json,@visx/hierarchy的运行时依赖非常克制:

  • d3-hierarchy(^1.1.4):布局计算引擎;
  • @visx/group:提供<Group>容器用于整体位移(top/left偏移);
  • classnames:合并 className;
  • @types/d3-hierarchy:TypeScript 类型支持。

其 peerDependencies 为react@^18 || ^19,并声明了 ESM/CJS 双入口(exports字段分别指向esm/index.jslib/index.js),支持 tree-shaking。

第一步:构造层级根节点

所有布局组件都接收一个root: HierarchyNode<Datum>属性——这与d3-hierarchy模块定义的根节点格式完全一致。因此第一步是把普通 JS 对象转换成 d3 层级结构:

import { hierarchy } from '@visx/hierarchy'; const root = hierarchy({ name: 'root', children: [ { name: 'child #1' }, { name: 'child #2', children: [{ name: 'grandchild #1' }, { name: 'grandchild #2' }, { name: 'grandchild #3' }], }, ], });

转换后的rootHierarchyNode<Datum>实例,携带depthheightparentchildrendescendants()links()等方法,可以直接传给任意布局组件。hierarchy默认从每个节点的children属性读取子节点,并可以通过第二个参数自定义 accessor:

const root = hierarchy(data, (d) => d.subItems);

此外,包还再导出了stratify,适合把扁平记录(如{ id, parentId }列表)组装成层级结构,例如数据库中的目录表或 CSV 导入的组织数据。

五种布局组件:适用场景与完整 Props

五种布局组件在 API 设计上高度一致:都接收roottopleftclassName,都支持children渲染回调覆盖默认输出,也都允许通过nodeComponent/linkComponent注入自定义渲染组件。下面逐一展开。

Tree:经典树状图

Tree对应d3-hierarchytree()布局,将树节点按“兄弟在同一水平层、叶子平铺”的方式排列,是最常用的目录树 / 组织架构图布局。核心 props(src/hierarchies/Tree.tsx):

Prop类型说明
rootHierarchyNode<Datum>必填,d3 层级根节点
size[number, number]布局尺寸[width, height]。坐标系统是任意的,例如径向布局可传[360, radius]表示 360° 广度、radius 深度
nodeSize[number, number]每个节点的固定尺寸[width, height];设置后根节点固定在⟨0, 0⟩
separation(a, b) => number相邻节点分离度 accessor,控制兄弟/表亲节点间距
linkComponent/nodeComponentReact 组件自定义连线/节点渲染
children(node) => ReactNode渲染回调,接收布局结果HierarchyPointNode<Datum>
top/leftnumber整体位移,作用于外层<Group>
classNamestring追加到外层<Group>的 class(默认带visx-tree

最简单的用法是让组件用默认节点与连线完成渲染:

import { Tree, hierarchy } from '@visx/hierarchy'; const data = hierarchy({ name: 'root', children: [{ name: 'a' }, { name: 'b' }] }); export default function SimpleTree() { return ( <svg width={500} height={300}> <Tree top={20} left={20} size={[460, 260]} root={data} /> </svg> ); }

Cluster:集群图(叶节点等距)

Cluster对应d3-hierarchycluster()布局。它与Tree的区别在于:所有叶节点被等距排布在底层,父节点居于子节点正中,非常适合表现具有明显层级且叶子数量可观的“谱系/聚类”结构。其 props 与Tree几乎一致(src/hierarchies/Cluster.tsx):sizenodeSizeseparationlinkComponentnodeComponentchildrentopleftclassName

源码中有一处细节值得注意:Treeif (nodeSize)判断是否设置nodeSize,而Cluster使用if (nodeSize !== undefined),允许显式传入[0, 0]这类假值场景,细节处理上更严谨。

Pack:圆形打包图(气泡图)

Pack对应d3-hierarchypack()布局,用大小不一的圆表示节点,通过圆面积体现数值大小,适合展示“部分与整体”的占比关系。核心 props(src/hierarchies/Pack.tsx):

Prop类型说明
radius(node) => number叶子圆半径 accessor;若不传,半径由叶子节点的value推导并按布局size等比缩放
size[number, number]布局尺寸[width, height]
paddingnumber节点间的近似间距
nodeComponentReact 组件自定义节点渲染(节点为HierarchyCircularNode,含xyr

注意Pack的输出节点是HierarchyCircularNode类型,默认节点组件HierarchyDefaultNode恰好兼容:它读取node.xnode.ynode.r渲染一个<circle>(见 HierarchyDefaultNode.tsx,默认半径 15、填充色#21D4FD)。因此PackTree/Cluster可共用同一个默认节点组件。

Partition:分区图(旭日图 / 冰柱图)

Partition对应d3-hierarchypartition()布局,把节点空间按 value 递归切分为矩形切片,横向或纵向堆叠,是**旭日图(Sunburst)冰柱图(Icicle)**的底层布局。核心 props(src/hierarchies/Partition.tsx):

Prop类型说明
size[number, number]布局尺寸
roundboolean是否对坐标取整(避免亚像素渲染模糊)
paddingnumber相邻子节点之间的间隔
nodeComponentReact 组件自定义节点渲染(节点为HierarchyRectangularNode,含x0/x1/y0/y1

Partition默认使用HierarchyDefaultRectNode(HierarchyDefaultRectNode.tsx),它从节点的x0、x1、y0、y1计算widthheight并渲染<rect>。若需要旭日图效果,只需把size设为极坐标语义的[2 * Math.PI, radius],再把矩形节点替换为弧形渲染即可。

Treemap:矩形树图

Treemap对应d3-hierarchytreemap()布局,将矩形区域按 value 递归切块,是磁盘占用分析、商品销售结构等场景的标准选择,也是本包中props 最丰富的组件(src/hierarchies/Treemap.tsx):

Prop类型说明
tileTileMethod<Datum>平铺算法,包内置再导出treemapSquarify(默认)、treemapBinarytreemapResquarifytreemapDicetreemapSlicetreemapSliceDice六种
size[number, number]布局尺寸
roundboolean是否取整坐标
paddingnumber \| accessor同时设置内、外 padding
paddingInnernumber \| accessor相邻子节点间距
paddingOuternumber \| accessor节点与其子节点的间距
paddingTop/paddingRight/paddingBottom/paddingLeftnumber \| accessor四个方向单独控制的边距
nodeComponentReact 组件自定义节点渲染(HierarchyRectangularNode

实现上(Treemap.tsx#L73-L83),padding系列参数统一经由 utils/setNumOrNumAccessor.ts 处理:无论你传一个数字还是返回数字的 accessor 函数,都会正确绑定到 d3 treemap 布局上。这意味着你完全可以按节点属性(如node.depthnode.value)动态决定边距:

import { Treemap, hierarchy, treemapSquarify } from '@visx/hierarchy'; const root = hierarchy(data).sum((d) => d.value); <Treemap root={root} tile={treemapSquarify} size={[500, 300]} round paddingInner={2} paddingOuter={4} nodeComponent={({ node }) => ( <rect x={node.x0} y={node.y0} width={node.x1 - node.x0} height={node.y1 - node.y0} fill={node.depth === 0 ? '#21D4FD' : '#b6fbff'} stroke="#fff" /> )} />;

注意这里调用了root.sum((d) => d.value)——sum()是 treemap、pack、partition 布局计算数值的前置步骤,它把每个节点的value汇总到祖先节点,布局算法正是依据这些 value 分配面积。这也是HierarchyNode与普通对象最大的差异点之一。

渲染回调(children)与自定义组件

五种布局组件都支持两条渲染路径,源码结构完全一致:

const data = layout(root); if (children) return <>{children(data)}</>; // 渲染回调优先 return ( <Group top={top} left={left} className={cx('visx-tree', className)}> {/* 默认遍历 links() 与 descendants() 渲染 */} </Group> );

(以 Tree.tsx#L68-L87 为例。)

  • 渲染回调模式:传入children后,组件只负责计算布局,把HierarchyPointNode(或HierarchyCircularNode/HierarchyRectangularNode)交给你自由渲染。这是构建交互式、动画化、异形节点图表的首选方式,典型用法如径向树图:
<Tree root={root} size={[2 * Math.PI, 400]}> {(tree) => ( <Group> {tree.links().map((link, i) => ( <path key={i} d={`M${link.source.x},${link.source.y} A... `} // 用弧线连接父子 fill="none" stroke="#999" /> ))} {tree.descendants().map((node, i) => ( <circle key={i} cx={node.x} cy={node.y} r={node.depth === 0 ? 8 : 4} fill="#21D4FD" /> ))} </Group> )} </Tree>
  • 默认渲染模式:不传children时,组件遍历data.links()data.descendants(),用linkComponentnodeComponent逐个渲染。三个默认组件可直接复用或作为自定义组件的起点:

    • HierarchyDefaultLink(HierarchyDefaultLink.tsx):渲染一条<line>strokeWidth=2stroke="#999"strokeOpacity=0.6
    • HierarchyDefaultNode:渲染<circle>r=15fill="#21D4FD",兼容x/y/rx/y两种节点结构;
    • HierarchyDefaultRectNode:渲染<rect>,由x0/x1/y0/y1计算宽高。

所有默认渲染都包在@visx/group<Group>中,且每种布局有独立的 className 前缀(visx-treevisx-clustervisx-packvisx-partitionvisx-treemap),便于全局样式覆盖。

小结:从数据到图表的四步路径

综合以上内容,用@visx/hierarchy做层次可视化的固定套路可以归纳为四步:

  1. 构造层级:用hierarchy(data)(或stratify(rows))把原始数据变成HierarchyNode
  2. 汇总数值(仅面积类布局需要):调用.sum((d) => d.value)让祖先节点累加 value;
  3. 选择布局:树状结构用Tree/Cluster,面积占比用Pack/Partition/Treemap,并通过sizepaddingtile等 props 调参;
  4. 定制渲染:要么用默认节点/连线组件快速出图,要么用children渲染回调完全掌控输出,配合自定义组件实现径向树、旭日图等高级形态。

仓库配套的测试(如 test/Tree.test.tsx、test/Cluster.test.tsx、test/Defaults.test.tsx)覆盖了布局坐标、默认组件渲染与节点/连线计数等行为,可作为你自定义实现时的行为参考。此外,visx-demo包的 sandboxes 目录中有各布局的完整可运行示例,packages/visx-demo/src/sandboxes 下的 tree、treemap、pack、partition 等示例可直接对照学习。

由于布局完全依赖d3-hierarchy的确定性算法,同一份root在不同布局间切换时数据层零改动——这也是本包“以文档规定的 d3 根节点为唯一数据契约”设计理念的最大红利。

【免费下载链接】visx🐯 visx | visualization components项目地址: https://gitcode.com/gh_mirrors/vi/visx

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询