vue实现网状关系图
使用 Vue 和 D3.js 实现网状关系图
D3.js 是一个强大的数据可视化库,结合 Vue 可以高效实现网状关系图。以下是一个基于 Force-Directed Graph(力导向图)的实现方法。
安装依赖:
npm install d3
组件代码示例:
<template>
<div ref="networkChart" class="network-container"></div>
</template>
<script>
import * as d3 from 'd3';
export default {
props: {
nodes: { type: Array, default: () => [] },
links: { type: Array, default: () => [] }
},
mounted() {
this.drawNetwork();
},
methods: {
drawNetwork() {
const container = this.$refs.networkChart;
const width = container.clientWidth;
const height = 600;
const simulation = d3.forceSimulation(this.nodes)
.force("link", d3.forceLink(this.links).id(d => d.id).distance(100))
.force("charge", d3.forceManyBody().strength(-300))
.force("center", d3.forceCenter(width / 2, height / 2));
const svg = d3.select(container)
.append("svg")
.attr("width", width)
.attr("height", height);
const link = svg.append("g")
.selectAll("line")
.data(this.links)
.enter().append("line")
.attr("stroke", "#999")
.attr("stroke-width", 2);
const node = svg.append("g")
.selectAll("circle")
.data(this.nodes)
.enter().append("circle")
.attr("r", 10)
.attr("fill", "#69b3a2")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
node.append("title").text(d => d.id);
simulation.on("tick", () => {
link
.attr("x1", d => d.source.x)
.attr("y1", d => d.source.y)
.attr("x2", d => d.target.x)
.attr("y2", d => d.target.y);
node
.attr("cx", d => d.x)
.attr("cy", d => d.y);
});
function dragstarted(event) {
if (!event.active) simulation.alphaTarget(0.3).restart();
event.subject.fx = event.subject.x;
event.subject.fy = event.subject.y;
}
function dragged(event) {
event.subject.fx = event.x;
event.subject.fy = event.y;
}
function dragended(event) {
if (!event.active) simulation.alphaTarget(0);
event.subject.fx = null;
event.subject.fy = null;
}
}
}
};
</script>
<style>
.network-container {
width: 100%;
border: 1px solid #eee;
}
</style>
使用 Vue 和 vis-network 实现
vis-network 是专门用于网络图可视化的库,使用更简单:
安装依赖:
npm install vis-network
组件实现:
<template>
<div ref="network" class="network-container"></div>
</template>
<script>
import { Network } from "vis-network";
export default {
props: {
nodes: { type: Array, default: () => [] },
edges: { type: Array, default: () => [] }
},
mounted() {
this.initNetwork();
},
methods: {
initNetwork() {
const container = this.$refs.network;
const data = {
nodes: this.nodes,
edges: this.edges
};
const options = {
nodes: {
shape: "dot",
size: 16,
font: {
size: 14
}
},
edges: {
width: 2,
smooth: true
},
physics: {
enabled: true,
stabilization: {
iterations: 100
}
}
};
new Network(container, data, options);
}
}
};
</script>
<style>
.network-container {
width: 100%;
height: 600px;
border: 1px solid #eee;
}
</style>
数据结构示例
两种实现方式都需要特定的数据结构:
// D3.js 格式数据
{
nodes: [
{ id: "Node 1" },
{ id: "Node 2" },
{ id: "Node 3" }
],
links: [
{ source: "Node 1", target: "Node 2" },
{ source: "Node 2", target: "Node 3" }
]
}
// vis-network 格式数据
{
nodes: [
{ id: 1, label: "Node 1" },
{ id: 2, label: "Node 2" },
{ id: 3, label: "Node 3" }
],
edges: [
{ from: 1, to: 2 },
{ from: 2, to: 3 }
]
}
交互功能增强
可以添加以下交互功能:
- 节点点击事件处理
- 鼠标悬停显示详细信息
- 缩放和平移控制
- 动态添加/删除节点
- 分组和颜色编码
对于大型数据集,建议:
- 使用Web Worker进行数据处理
- 实现虚拟渲染(只渲染可见区域)
- 添加加载状态指示器
性能优化建议
当节点数量超过500时:
- 减少物理模拟的迭代次数
- 简化节点渲染(使用简单形状)
- 禁用阴影等视觉效果
- 分批次渲染数据
这两种方案各有优势:D3.js更灵活适合定制化需求,vis-network更简单适合快速实现标准网络图。根据项目复杂度选择合适方案。







