数据管理系统
目的与范围
数据管理系统负责在多个 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_W与el_H(元素宽/高) -
默认
expanded: true、rgCalcedVisibility: true、rgShouldRender: 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
该方法:
- 通过
Object.assign()将新值合并到this.options - 处理性能模式下视口变化的特殊情况
- 设置
commits.optionsChanged = true - 不会自动调用
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