JavaScript is required

数据管理系统

目的与范围

数据管理系统负责在多个 JavaScript 框架中存储、更新并同步所有图数据(nodes、lines、links)到 UI 层。它提供一个与框架无关的核心(core),并为 Vue 2、Vue 3、React 和 Svelte 提供响应式适配器,实现用户交互与可视化渲染之间的双向数据流。

关于具体 CRUD 操作的信息,请参见 CRUD 操作与状态更新。关于框架特定的响应式集成细节,请参见 响应式数据集成。关于查询与分析能力,请参见 图分析与查询


架构概览

数据管理系统遵循分层架构:包含一个与框架无关的核心(RGDataProvider)以及处理响应式更新的框架特定适配器。

graph TB
    subgraph "框架层"
        Vue2["RelationGraphWith2Data
setReactiveData4Vue2()"] Vue3["RelationGraphWith2Data
setReactiveData4Vue3()"] React["RelationGraphWith2Data
setReactiveData4React()"] Svelte["RelationGraphWith2Data
setReactiveData4Svelte()"] end subgraph "核心数据管理" DP["RGDataProvider
(extends RGDataGetter)"] DPVue2["RGDataProvider4Vue2"] DPVue3["RGDataProvider4Vue3"] DPReact["RGDataProvider4React"] DPSvelte["RGDataProvider4Svelte"] end subgraph "数据结构" GraphData["RGGraphData
{nodes, normalLines, fakeLines, rootNode}"] Options["RGOptionsFull
{layout, zoom, offset, editing...}"] Runtime["运行时映射
runtimeDATA4NodeMap
runtimeDATA4LinkMap
runtimeDATA4Links"] ShouldRender["渲染列表
shouldRenderNodes
shouldRenderLines
shouldRenderFakeLines"] end subgraph "更新机制" Commits["DataCommits
{changedNodes, changedLines,
nodesListChanged...}"] UpdateHook["updateViewHook()
触发框架重渲染"] end Vue2 --> DPVue2 Vue3 --> DPVue3 React --> DPReact Svelte --> DPSvelte DPVue2 --> DP DPVue3 --> DP DPReact --> DP DPSvelte --> DP DP --> GraphData DP --> Options DP --> Runtime DP --> ShouldRender DP --> Commits Commits --> UpdateHook UpdateHook --> Vue2 UpdateHook --> Vue3 UpdateHook --> React UpdateHook --> Svelte
  • packages/relation-graph-models/models/RelationGraphWith2Data.ts:1-125
  • packages/relation-graph-models/data/RGDataProvider.ts:1-60

核心数据结构

RGGraphData

RGDataProvider 维护的所有图元素的主要容器:

属性 类型 描述
nodes RGNode[] 图中的所有节点
normalLines RGLine[] 连接节点的标准线
fakeLines RGFakeLine[] 用于元素连接的虚拟线
rootNode RGNode | undefined 布局使用的指定根节点
  • packages/relation-graph-models/data/RGDataProvider.ts:1-11

运行时数据映射

用于快速查找与关系跟踪:

graph LR
    subgraph "RGDataProvider 内部存储"
        NodeMap["runtimeDATA4NodeMap
Map<nodeId, RGNode>"] LinkMap["runtimeDATA4LinkMap
Map<lineId, RGLink>"] Links["runtimeDATA4Links
RGLink[]"] ElTargets["runtimeDATA4ElLineTargets
RGLineTarget[]"] ConnectTargets["runtimeDATA4ConnectTargets
RGConnectTargetData[]"] end NodeMap -.->|"O(1) 查找"| Node["RGNode"] LinkMap -.->|"O(1) 查找"| Link["RGLink"] Links --> FromNode["fromNode: RGNode"] Links --> ToNode["toNode: RGNode"] ElTargets -.->|"HTML 元素"| HTMLEl["DOM Element"] ConnectTargets -.->|"连接点"| NodeOrCanvas["Node or Canvas"]
  • packages/relation-graph-models/data/RGDataGetter.ts(基类)
  • packages/relation-graph-models/data/RGDataProvider.ts:45-55

数据提供者系统

RGDataProvider 类层级结构

graph TB
    RDG["RGDataGetter
只读查询方法"] RDP["RGDataProvider
CRUD + 提交跟踪"] RDG --> RDP RDP4V2["RGDataProvider4Vue2
Vue.set() 响应式"] RDP4V3["RGDataProvider4Vue3
ref/reactive API"] RDP4R["RGDataProvider4React
setState hook"] RDP4S["RGDataProvider4Svelte
writable stores"] RDP --> RDP4V2 RDP --> RDP4V3 RDP --> RDP4R RDP --> RDP4S

框架适配器初始化

每个框架在 RelationGraphWith2Data 中都有专用的初始化方法:

框架 方法 数据提供者类 关键特性
Vue 2 setReactiveData4Vue2() RGDataProvider4Vue2 使用 Vue.set() 实现响应式
Vue 3 setReactiveData4Vue3() RGDataProvider4Vue3 使用 ref()reactive()
React setReactiveData4React() RGDataProvider4React 调用 updateViewHook() 触发 setState
Svelte setReactiveData4Svelte() RGDataProvider4Svelte 使用带更新器的 writable stores
  • packages/relation-graph-models/models/RelationGraphWith2Data.ts:31-110
  • packages/relation-graph-models/data/RGDataProvider4Vue2.ts
  • packages/relation-graph-models/data/RGDataProvider4Vue3.ts
  • packages/relation-graph-models/data/RGDataProvider4React.ts
  • packages/relation-graph-models/data/RGDataProvider4Svelte.ts

基于提交(Commit)的更新机制

DataCommits 结构

系统通过一个提交对象来跟踪变更,该对象记录哪些数据被修改过:

type DataCommits = {
    optionsChanged: boolean,
    changedNodes: string[],        // 发生变化的节点 ID
    nodesListChanged: boolean,      // 结构性变化(添加/移除)
    changedLines: string[],
    linesListChanged: boolean,
    changedFakeLines: string[],
    fakeLinesListChanged: boolean,
    changedConnectTargets: string[],
    connectTargetsListChanged: boolean
}
  • packages/relation-graph-models/data/RGDataProvider.ts:19-29

更新流程图

sequenceDiagram
    participant User
    participant API as "公共 API
(addNode, updateNode...)" participant DP as "RGDataProvider" participant Commits as "DataCommits" participant Hook as "updateViewHook()" participant Framework as "框架
(Vue/React/Svelte)" User->>API: addNode(nodeJson) API->>DP: addNodes([rgNode]) DP->>DP: graphData.nodes.push(rgNode) DP->>DP: runtimeDATA4NodeMap.set(id, node) DP->>Commits: commits.nodesListChanged = true API->>DP: dataUpdated() DP->>DP: const commits = this.commits DP->>DP: this.commits = resetCommits() DP->>Hook: updateViewHook(commits) Hook->>Framework: 触发重渲染 Framework-->>User: UI 更新
  • packages/relation-graph-models/data/RGDataProvider.ts:109-122
  • packages/relation-graph-models/models/RelationGraphWith2Data3Update.ts:385-416

提交跟踪方法

RGDataProvider 中用于跟踪变更的关键方法:

方法 目的 变更行
commitNode(nodeId) 标记某个节点发生变化 packages/relation-graph-models/data/RGDataProvider.ts:85-96
commitLine(lineId) 标记某条线发生变化 packages/relation-graph-models/data/RGDataProvider.ts:79-84
commitFakeLine(lineId) 标记某条虚拟线发生变化 packages/relation-graph-models/data/RGDataProvider.ts:97-102
dataUpdated() 执行所有提交并触发视图更新 packages/relation-graph-models/data/RGDataProvider.ts:109-122

数据同步流程

CRUD 操作管线

graph LR
    subgraph "输入层"
        JsonNode["JsonNode
{id, text, x, y...}"] JsonLine["JsonLine
{from, to, text...}"] end subgraph "转换" J2N["json2Node()
转换并校验"] J2L["json2Line()
转换并校验"] end subgraph "数据提供者操作" AddNode["addNodes(RGNode[])"] AddLine["addLines(RGLine[])"] UpdateNode["updateNode(id, updates)"] RemoveNode["removeNodeById(ids)"] end subgraph "存储更新" NodeArr["graphData.nodes[]"] LineArr["graphData.normalLines[]"] NodeMap["runtimeDATA4NodeMap"] LinkUpdate["updateLinks()
重建 RGLink[]"] end subgraph "渲染管线" Commits["提交跟踪"] ViewUpdate["updateViewHook()"] ShouldRender["updateShouldRenderGraphData()
(性能模式)"] end JsonNode --> J2N JsonLine --> J2L J2N --> AddNode J2L --> AddLine AddNode --> NodeArr AddNode --> NodeMap AddLine --> LineArr AddLine --> LinkUpdate UpdateNode --> NodeMap RemoveNode --> NodeArr RemoveNode --> NodeMap NodeArr --> Commits LineArr --> Commits LinkUpdate --> Commits Commits --> ViewUpdate Commits --> ShouldRender
  • packages/relation-graph-models/models/RelationGraphWith2Data3Update.ts:385-456
  • packages/relation-graph-models/data/RGNodeDataUtils.ts:10-96
  • packages/relation-graph-models/data/RGLineDataUtils.ts

关键转换函数

json2Node() - 节点创建

将用户提供的 JSON 转换为带默认值的内部 RGNode 格式:

json2Node(originData: JsonNode, graphOptions?: RGOptions): RGNode

关键转换:

  • 校验必填的 id 字段

  • graphOptions 应用默认节点形状、宽度、高度

  • 初始化 lot(布局跟踪对象),其 children 数组为空

  • 设置用于渲染的 el_Wel_H(元素宽/高)

  • 默认 expanded: truergCalcedVisibility: truergShouldRender: true

  • packages/relation-graph-models/data/RGNodeDataUtils.ts:10-96

json2Line() - 连线创建

将用户提供的连线 JSON 转换为内部 RGLine 格式:

关键转换:

  • 若未提供则生成唯一 id

  • graphOptions 应用默认线条形状、颜色、宽度

  • 规范化 from/to(也接受 source/target 别名)

  • 设置折点、线条形状、箭头可见性

  • 初始化用于双向渲染的 isReverse 标志

  • packages/relation-graph-models/data/RGLineDataUtils.ts


性能优化

使用 shouldRender 列表进行视口裁剪

对于大图(>1000 节点),系统维护可见元素的过滤列表:

graph TB
    subgraph "完整数据"
        AllNodes["graphData.nodes
(所有节点)"] AllLines["graphData.normalLines
(所有连线)"] AllFake["graphData.fakeLines
(所有虚拟线)"] end subgraph "更新触发" Trigger["Canvas offset/zoom 变化
或节点位置更新"] end subgraph "裁剪算法" UpdateMethod["updateShouldRenderGraphData()"] ViewBox["计算视口边界
考虑 zoom 与 offset"] Filter["过滤位于视口内的
节点/连线"] end subgraph "优化后的渲染列表" RenderNodes["shouldRenderNodes[]
(仅可见节点)"] RenderLines["shouldRenderLines[]
(仅可见连线)"] RenderFake["shouldRenderFakeLines[]
(可见虚拟线)"] end subgraph "UI 组件" Canvas["RGCanvas components
遍历 shouldRender* 列表"] end AllNodes --> UpdateMethod AllLines --> UpdateMethod AllFake --> UpdateMethod Trigger --> UpdateMethod UpdateMethod --> ViewBox ViewBox --> Filter Filter --> RenderNodes Filter --> RenderLines Filter --> RenderFake RenderNodes --> Canvas RenderLines --> Canvas RenderFake --> Canvas

关键方法: updateShouldRenderGraphData()

该方法在以下情况下被调用:

  • Canvas offset 变化(平移)

  • Canvas zoom 变化

  • 节点位置更新(拖拽、布局)

  • 启用性能模式(options.performanceMode

  • packages/relation-graph-models/data/RGDataProvider.ts:129-145

  • packages/relation-graph-models/models/RelationGraphWith2Data.ts:36-63

数据存储结构

dataStores 对象为各框架提供对响应式容器的直接引用:

dataStores: {
    optionsStore: any,                    // 主 options 对象
    shouldRenderNodesStore: any,          // 裁剪后的节点列表
    shouldRenderLinesStore: any,          // 裁剪后的连线列表
    shouldRenderFakeLinesStore: any,      // 裁剪后的虚拟线列表
    optionsRef?: any,                     // Vue 3 ref 包装
    shouldRenderNodesRef?: any,           // Vue 3 ref 包装
    shouldRenderLinesRef?: any,           // Vue 3 ref 包装
    shouldRenderFakeLinesRef?: any        // Vue 3 ref 包装
}
  • packages/relation-graph-models/models/RelationGraphWith2Data.ts:84-93

节点与连线管理

节点 CRUD 操作

操作 方法 描述
创建 addNode(JsonNode) 将 JSON 转换为 RGNode 并添加到图中
批量创建 addNodes(JsonNode[]) 批量添加多个节点
读取 getNodeById(id) 通过 runtimeDATA4NodeMap 进行 O(1) 查找
更新 updateNode(id, Partial<RGNode>) 将更新合并到现有节点
删除 removeNodeById(id) 移除节点及其关联连线

重要: 节点移除会自动触发 beforeNodeBeRemove(),其将删除:

  • 与该节点相连的所有连线(from === nodeId || to === nodeId

  • 与该节点相连的所有虚拟线

  • 与该节点关联的 connect targets

  • packages/relation-graph-models/models/RelationGraphWith2Data3Update.ts:144-168

  • packages/relation-graph-models/data/RGDataProvider.ts:220-236

连线 CRUD 操作

操作 方法 描述
创建 addLine(JsonLine) 将 JSON 转换为 RGLine 并添加到图中
批量创建 addLines(JsonLine[]) 批量添加;自动区分 normal/fake lines
读取 getLineById(id) normalLines 数组中查找
更新 updateLine(id, Partial<RGLine>) 更新连线属性
删除 removeLineById(id) 移除连线及其关联的 RGLink

Link 生成: 添加连线后,updateLinks() 会创建 RGLink 对象,它们保存对实际 RGNode 对象的引用,以便快速渲染:

type RGLink = {
    fromNode: RGNode,
    toNode: RGNode,
    lineId: string,
    currentLineIndex: number,        // 用于多线间距
    totalLinesBetweenNodes: number   // 相同节点之间的连线总数
}
  • packages/relation-graph-models/models/RelationGraphWith2Data3Update.ts:174-205
  • packages/relation-graph-models/data/RGDataProvider.ts:237-253

选项管理

RGOptionsFull 结构

options 对象控制所有图行为与可视状态:

graph TB
    subgraph "配置"
        Layout["layout: RGLayoutOptions
{layoutName, direction, gaps...}"] Visual["视觉设置
defaultNodeColor, defaultLineShape..."] Interaction["交互
disableDragNode, canvasMoveMode..."] end subgraph "运行时状态" Canvas["Canvas 状态
canvasZoom, canvasOffset, viewSize"] Checked["选择状态
checkedNodeId, checkedLineId"] Editing["编辑状态
editingController, editingLineController"] end subgraph "功能开关" Performance["performanceMode, showEasyView"] Animation["enableNodeXYAnimation, enableCanvasTransformAnimation"] UI["showToolBar, showMiniView, showReferenceLine"] end Options["RGOptionsFull"] --> Layout Options --> Visual Options --> Interaction Options --> Canvas Options --> Checked Options --> Editing Options --> Performance Options --> Animation Options --> UI

默认创建: createDefaultConfig() 会生成具有合理默认值的 options:

  • packages/relation-graph-models/data/RGOptionsDataUtils.ts:15-242
  • packages/relation-graph-models/models/RelationGraphWith3Options1Update.ts:21-58

更新选项

选项更新通过 RGDataProvider.updateOptions() 进行:

updateOptions(options: Partial<RGOptionsFull>): void

该方法:

  1. 通过 Object.assign() 将新值合并到 this.options
  2. 处理性能模式下视口变化的特殊情况
  3. 设置 commits.optionsChanged = true
  4. 不会自动调用 dataUpdated()(需要调用方触发)
  • packages/relation-graph-models/data/RGDataProvider.ts:123-146

数据生命周期示例

添加节点与连线的完整流程:

sequenceDiagram
    participant User as "用户代码"
    participant API as "graphInstance"
    participant With2Data3 as "RelationGraphWith2Data3Update"
    participant DP as "RGDataProvider"
    participant Maps as "内部映射"
    participant View as "UI 组件"
    
    Note over User,View: 1. 添加节点
    User->>API: addNode({id:'A', text:'Node A'})
    API->>With2Data3: _addNodes([jsonNode])
    With2Data3->>With2Data3: json2Node(jsonNode, options)
    With2Data3->>DP: addNodes([rgNode])
    DP->>Maps: graphData.nodes.push(rgNode)
    DP->>Maps: runtimeDATA4NodeMap.set('A', rgNode)
    DP->>DP: commits.nodesListChanged=true
    With2Data3->>DP: _dataUpdated()
    DP->>DP: dataUpdated()
    DP->>View: updateViewHook(commits)
    View-->>User: 节点渲染
    
    Note over User,View: 2. 添加连线
    User->>API: addLine({from:'A', to:'B'})
    API->>With2Data3: _addLines([jsonLine])
    With2Data3->>With2Data3: json2Line(jsonLine, options)
    With2Data3->>DP: addLines([rgLine])
    DP->>Maps: graphData.normalLines.push(rgLine)
    DP->>DP: updateLinks()
    DP->>Maps: Create RGLink{fromNode, toNode}
    DP->>Maps: runtimeDATA4Links.push(link)
    DP->>Maps: runtimeDATA4LinkMap.set(lineId, link)
    DP->>DP: commits.linesListChanged=true
    With2Data3->>DP: _dataUpdated()
    DP->>DP: dataUpdated()
    DP->>View: updateViewHook(commits)
    View-->>User: 连线渲染
  • packages/relation-graph-models/models/RelationGraphWith2Data3Update.ts:144-204
  • packages/relation-graph-models/data/RGDataProvider.ts:109-122