展开节点时按需加载子节点
这是一个自上而下树图示例:在折叠分支展开时按需懒加载子节点。它把返回的图数据片段追加到实时 relation-graph 实例中,重新执行布局,并同时展示页面级与节点级加载反馈。
通过延迟加载子节点来展开树
这个示例实现了什么
这个示例构建了一棵自上而下的树,当用户展开已标记的节点时,树会继续增长。初始图数据被刻意保持得较小,然后在某个折叠分支按需请求更多后代节点时,把它们追加到现有的 relation-graph 实例中,而不是替换整份数据集。
在界面上,用户会看到米色矩形节点、正交连接线、位于底部的展开控件,以及悬浮在画布上方的白色工具窗口。在异步展开期间,页面会显示加载遮罩,当前活动节点也会显示自己的 spinner 徽标,因此很容易识别正在解析的分支。
这个演示最有价值的点不只是延迟加载本身,而是一次性分支加载、对新追加后代递归重复这一模式,以及在插入新片段后显式重新布局这三者的组合。
数据如何组织
这张图使用 RGJsonData,包含一个 rootId、一个扁平的 nodes 数组和一个扁平的 lines 数组。初始数据集在 initializeGraph() 中创建,其中有一个节点预先标记了 expanded: false、expandHolderPosition: 'bottom' 和 data.isNeedLoadDataFromRemoteServer: true,从而让展开动作成为加载触发器。
在首次调用 setJsonData(...) 之前,并没有做复杂的预处理。这个示例直接在代码中构造初始树,将其加载到图中,然后让视图居中并适配画布。异步分支加载器同样返回普通的 nodes 和 lines,这意味着同样的模式也可以由 API 响应、数据库查询、文件夹树、依赖树,或任何能表示为图片段的层级来源来支撑。
在插入追加节点之前,示例会把被展开节点的 x 和 y 坐标复制到每一个新节点上。这个小步骤让后续的 doLayout() 看起来像是从被点击的分支展开出来,而不是从一个无关位置对整张图进行重建。
relation-graph 是如何使用的
这个示例挂载在 RGProvider 中,RGHooks.useGraphInstance() 是主要的控制入口。图配置了树形布局,包括 from: 'top'、treeNodeGapV: 120、矩形节点、正交线条、上下方向的连接点、底部展开占位器,以及针对普通展开或折叠变化的自动重新布局。
图实例 API 驱动了运行时行为。setJsonData(...) 加载初始树,moveToCenter() 和 zoomToFit() 准备首次视图,updateOptions(...) 把重新布局开关与 React 状态同步,updateNodeData(...) 为活动节点标记加载状态,addNodes(...) 和 addLines(...) 追加新的图片段,sleep(...) 在布局前制造一个短暂停顿,而 doLayout() 则会在异步插入后重新计算树布局。
自定义渲染保持聚焦且实用。RGSlotOnNode 负责渲染节点文本,并在当前加载中的节点下方按条件添加一个 spinner 气泡。外围的 DraggableWindow 辅助组件则提供了一个悬浮说明区域和一个设置面板,可以修改滚轮行为、修改画布拖拽行为,并通过 prepareForImageGeneration() 和 restoreAfterImageGeneration() 导出图片。
本地 SCSS 文件非常精简,看起来并没有定义渲染图形的主要视觉风格。这个示例中大部分可见样式都来自图配置、工具类以及自定义节点插槽。
关键交互
- 展开带有
data.isNeedLoadDataFromRemoteServer的节点时,会触发异步子节点生成流程。 - 同一个节点在后续再次展开时不会重复追加内容,因为
childrenLoaded会被回写到节点数据中。 - 新追加的后代节点可以继续沿用这一模式,因为返回结果中的一个子节点也被标记为折叠且可远程加载。
- 悬浮开关会更新
reLayoutWhenExpandedOrCollapsed,用于控制普通的展开和折叠行为;而辅助说明文字则明确指出,动态追加的内容仍然会强制触发重新布局。 - 悬浮窗口中的设置面板可以修改滚轮模式、修改画布拖拽模式,并下载图像。
- 工具窗口本身也可以拖动和最小化,因此说明层不会遮挡图表区域。
关键代码片段
这份初始数据集展示了初始树如何嵌入一个可进行延迟加载的折叠分支。
const myJsonData: RGJsonData = {
rootId: 'a',
nodes: [
{ id: 'a', text: 'a' },
{ id: 'b', text: 'b' },
{ id: 'b1', text: 'b1' },
{ id: 'b2', text: 'b2' },
{ id: 'b2-1', text: 'b2-1', expandHolderPosition: 'bottom', expanded: false, data: { isNeedLoadDataFromRemoteServer: true } },
{ id: 'b2-2', text: 'b2-2' }
],
这段守卫逻辑说明,只有被标记的节点才会加载子节点,而且每个分支只会追加一次远程风格的数据。
if (!node.data.isNeedLoadDataFromRemoteServer) {
return;
}
if (node.data.childrenLoaded) {
return;
}
setLoading(true);
// Effect is equivalent to: node.data.childrenLoaded = true; updateNodeData method updates data to support reactivity
graphInstance.updateNodeData(node, {
childrenLoaded: true, // Mark node as: dynamic child nodes loaded
myLoading: true // Add style to node: loading
});
这段追加流程展示了核心运行时模式:把新节点放在被展开父节点附近,追加进去,短暂等待后重新执行布局。
newJsonData.nodes.forEach(newNode => {
newNode.x = node.x; // JsonNode object can directly modify properties before being added to graph, no need to use graphInstance.updateNode method
newNode.y = node.y;
});
// Use API to append data
graphInstance.addNodes(newJsonData.nodes);
graphInstance.addLines(newJsonData.lines);
await graphInstance.sleep(350);
await graphInstance.doLayout();
setLoading(false);
这个节点插槽把加载状态变成了当前正在展开分支上的一个内联视觉提示。
<RGSlotOnNode>
{({ node }: RGNodeSlotProps) => (
<div className="h-full w-full flex place-items-center justify-center">
{node.text}
{
node.data.myLoading && <div className="absolute rounded-full bottom-[-23px] w-[20px] h-[20px] bg-white border border-gray-300 flex place-items-center justify-center">
<Loader2Icon className="animate-spin" size={16} />
</div>
}
</div>
)}
</RGSlotOnNode>
这个运行时开关表明,展开或折叠时是否重新布局被视为一个实时选项,而不是固定在启动阶段的设置。
const setRelayoutWhenExpandedOrCollapsed = (newValue: boolean) => {
// 3.x recommends using updateOptions to modify config
graphInstance.updateOptions({ reLayoutWhenExpandedOrCollapsed: newValue });
setRelayout(newValue);
};
这个示例的独特之处
与附近同样体现延迟增长的示例(例如 expand-button)相比,这个演示更明确地聚焦于递归式的自上而下树增长,而不只是给占位叶子节点暴露展开控件。它会把一个新追加的子节点再次标记为未来的延迟加载目标,因此这种逐步揭示的模式可以继续向更深层推进,而不是在第一次展开后结束。
与 investment 和 investment-penetration 相比,这个示例让内容保持通用,节点视觉也相对朴素。这反而让可复用的经验更清晰:如何拦截展开动作、守卫一次性异步加载、使用 addNodes(...) 和 addLines(...) 追加图片段,以及同时提供全局和分支级别的加载反馈。
在同样复用悬浮辅助窗口的示例中,这个示例对分支级状态反馈和运行时重新布局偏好给予了不同寻常的强调。它的标志性组合是:暖色调的自上而下正交树、底部展开占位器、显式的异步追加并重新布局流程、页面级加载遮罩,以及直接在当前活动节点插槽中渲染的内联 spinner。
这一模式还适用于哪些场景
这一模式非常适合任何在一开始就完整加载会过大或成本过高的层级结构。常见例子包括按需获取子团队的组织架构图、只在打开时才请求文件夹内容的文件浏览器、按增量显示下游模块的依赖树,以及一次只展开一条因果分支的调查图谱。
当用户在异步展开期间需要清晰反馈时,它也同样有用。图级加载状态与节点级加载状态的组合非常适合知识探索工具、审批树、基础设施归属图以及其他这类界面,在这些场景中,用户需要先知道哪个分支仍在解析,然后才能继续导航。