Vue3 里怎么用 G6 做可视化?从环境到复杂交互全流程拆解
Vue3 项目里怎么安装和配置 G6?
要在 Vue3 项目里用 G6,第一步得把 G6 装到项目中,不管你用 Vite 还是 Vue CLI 创建项目,打开终端用包管理器安装即可,用 npm 的话,执行 npm install @antv/g6 --save;用 yarn 则是 yarn add @antv/g6。
安装时要留意版本兼容性,G6 稳定版是 4.x 系列,和 Vue3 的 Composition API、响应式系统适配良好,G6 依赖浏览器环境运行,若项目用了 SSR(服务端渲染),要在客户端 DOM 挂载后再初始化 G6——否则服务端执行代码时因无 DOM 环境会报错,把 G6 初始化逻辑放到 onMounted 钩子中,确保只在浏览器端执行。
怎么在 Vue3 组件里初始化 G6 的图实例?
用 <script setup> 语法的 Vue3 组件,初始化 G6 图实例分三步:在模板放 DOM 容器、用 ref 获取容器、在 onMounted 里初始化 Graph 实例。
看个示例代码:
<script setup>
import { ref, onMounted } from 'vue'
import G6 from '@antv/g6'
const graphContainer = ref(null) // 用于获取 DOM 容器的 ref
let graph = null // 存储 Graph 实例,方便后续操作
onMounted(() => {
// DOM 挂载完成后才能初始化 G6(G6 需要容器节点)
const container = graphContainer.value
graph = new G6.Graph({
container, // 绑定 DOM 容器
width: 800, // 图的宽度
height: 600, // 图的高度
modes: { // 配置交互模式,比如允许拖拽、缩放画布
default: ['drag-canvas', 'zoom-canvas']
}
})
})
</script>
<template>
<!-- 给 G6 当容器的 div,必须设置宽高 -->
<div ref="graphContainer" class="graph-box"></div>
</template>
<style scoped>
.graph-box {
width: 800px;
height: 600px;
border: 1px solid #eee;
}
</style>
关键在于:G6 需等 DOM 渲染完再初始化,所以逻辑要放 onMounted 里;且容器 div 必须有明确宽高,否则图无法渲染。
Vue3 的响应式数据怎么驱动 G6 渲染?
G6 绘图需 nodes(节点)和 edges(边)数据(格式为数组),在 Vue3 中,可用 reactive 或 ref 包装数据为响应式,再传给 G6。
步骤 1:定义响应式数据
用 reactive 存储数据示例:
import { reactive } from 'vue'
const graphData = reactive({
nodes: [
{ id: 'node1', label: '节点1', x: 100, y: 100 },
{ id: 'node2', label: '节点2', x: 300, y: 200 }
],
edges: [
{ source: 'node1', target: 'node2' }
]
})
步骤 2:把数据传给 G6
在 onMounted 里,用 graph.data() 传数据、graph.render() 渲染:
onMounted(() => {
// 前面的初始化代码...
graph.data(graphData) // 设置数据
graph.render() // 渲染图
})
步骤 3:数据变化时更新图
若数据动态变化(如接口获取、用户操作修改),需监听数据变化触发 G6 更新,用 watch 实现:
import { watch } from 'vue'
watch(
() => [graphData.nodes, graphData.edges], // 监听 nodes 和 edges 变化
() => {
graph.data(graphData) // 重新设置数据
graph.render() // 重新渲染
},
{ deep: true } // 深监听,因数组内对象可能变化
)
若数据量极大,全量更新性能差,可改用 G6 增量更新 API(如 addNode、updateNode),仅更改变动部分以降低渲染压力。
怎么给 G6 加交互和事件,还能和 Vue 组件互动?
G6 本身交互能力丰富(如点击节点、拖拽、tooltip 等),可在 Graph 实例绑定事件,再与 Vue 组件状态/事件联动。
绑定基础事件(如点击节点)
在 onMounted 里给 graph 绑定 node:click 事件:
onMounted(() => {
// 初始化 graph...
graph.on('node:click', (event) => {
const node = event.item // 点击的节点实例
const nodeData = node.getModel() // 节点数据源
// 触发 Vue 组件自定义事件,传数据给父组件
emit('nodeClick', nodeData)
// 或修改组件内响应式状态
state.activeNodeId = nodeData.id
})
})
添加内置交互(如 tooltip)
G6 插件系统可扩展交互(如 Tooltip 插件),先导入插件再注册:
import { Tooltip } from '@antv/g6/plugins'
onMounted(() => {
const tooltip = new Tooltip({
offsetX: 10, // tooltip 相对鼠标的 X 偏移
offsetY: 10, // Y 偏移
getContent: (event) => { // 定义 tooltip 内容
const item = event.item
if (item) {
const model = item.getModel()
return `<div>${model.label}</div>` // 显示节点 label
}
return ''
}
})
graph.addPlugin(tooltip) // 把插件加入图中
})
节点 hover 时会显示 tooltip,交互逻辑还能结合 Vue 响应式,比如点击节点后显示侧边栏,数据存在 Vue 状态中,界面自动更新。
想复用 G6 图表,怎么封装成 Vue3 组件?
重复写 G6 初始化、数据处理代码太繁琐,封装通用组件可提升复用性,思路是:用 props 传配置(数据、宽高),用 emit 传事件(节点点击、边点击),内部处理初始化、更新、销毁。
看封装好的 <BaseGraph> 组件示例:
<script setup>
import { ref, onMounted, watch, onUnmounted } from 'vue'
import G6 from '@antv/g6'
const props = defineProps({
nodes: { type: Array, required: true }, // 节点数据
edges: { type: Array, required: true }, // 边数据
width: { type: Number, default: 800 }, // 图宽度
height: { type: Number, default: 600 } // 图高度
})
const emit = defineEmits(['nodeClick', 'edgeClick']) // 自定义事件
const graphContainer = ref(null)
let graph = null
onMounted(() => {
initGraph() // 初始化图
})
// 监听数据变化,更新图
watch(
() => [props.nodes, props.edges],
() => {
updateGraph()
},
{ deep: true }
)
// 组件销毁时,销毁 G6 实例释放内存
onUnmounted(() => {
if (graph) graph.destroy()
})
// 初始化 G6 的核心逻辑
function initGraph() {
const container = graphContainer.value
graph = new G6.Graph({
container,
width: props.width,
height: props.height,
modes: { default: ['drag-canvas', 'zoom-canvas'] }
})
// 绑定节点点击事件
graph.on('node:click', (e) => {
emit('nodeClick', e.item.getModel())
})
// 绑定边点击事件
graph.on('edge:click', (e) => {
emit('edgeClick', e.item.getModel())
})
updateGraph() // 渲染初始数据
}
// 更新图数据并渲染
function updateGraph() {
graph.data({ nodes: props.nodes, edges: props.edges })
graph.render()
}
</script>
<template>
<div ref="graphContainer" :style="{ width: width + 'px', height: height + 'px' }"></div>
</template>
其他组件使用时,直接传 nodes、edges 并监听事件:
<template>
<BaseGraph
:nodes="nodes"
:edges="edges"
@nodeClick="handleNodeClick"
/>
</template>
<script setup>
import BaseGraph from './BaseGraph.vue'
import { reactive } from 'vue'
const nodes = reactive([/* 节点数据 */])
const edges = reactive([/* 边数据 */])
function handleNodeClick(node) {
console.log('点击了节点:', node)
}
</script>
封装后重复代码减少,维护更轻松。
数据量大时,Vue3 和 G6 怎么优化性能?
节点、边数量上千时需做性能优化,可从 G6 和 Vue 两端着手:
G6 侧优化
- 增量更新:避免每次全量调用
graph.render(),改用 G6 增量 API(如updateItem修改单个节点、addNode新增节点),减少无效渲染。 - 内置优化能力:G6 支持“分组渲染”“虚拟节点”,适合大数据场景,节点极多时,开启
enableSvgTranslate: false减少 SVG 操作,或尝试 WebGL 渲染(需留意兼容性,WebGL 版本仍在迭代)。 - 销毁实例:组件卸载时调用
graph.destroy()释放内存,防止内存泄漏。
Vue 侧优化
- 响应式优化:处理大数组时,用
shallowReactive替代reactive——深层响应式会递归遍历所有子对象,性能开销大;shallowReactive仅处理顶层响应式,适合大数组场景。 - 减少不必要监听:用
watch时精准监听变化字段,避免深监听带来的性能损耗。
G6 的样式怎么和 Vue3 的 Scoped CSS 配合?
G6 节点样式分 SVG 绘制节点和自定义 HTML 节点两类,与 Vue Scoped CSS 配合需注意样式穿透。
SVG 节点的样式
SVG 节点样式可通过 G6 配置传入,比如给节点设 style:
graph.data({
nodes: [
{
id: 'node1',
label: '节点1',
style: { fill: '#1890ff', stroke: '#ccc' } // 直接设置 SVG 样式
}
]
})
自定义 HTML 节点的样式
若用自定义 HTML 节点(如 nodeRender 函数返回 HTML 字符串),Scoped CSS 默认对其无效(Scoped 给元素加 data-v-xxx 属性,G6 生成的 HTML 节点无此属性),此时用 :v-deep 穿透:
<style scoped>
::v-deep .g6-node-html {
color: #ff4d4f;
font-size: 14px;
}
</style>
也可把 G6 相关样式放全局样式文件(如 main.css)跳过 Scoped 限制,还能结合 CSS 变量,在 Vue 组件里动态改样式:
<template>
<div class="graph-wrapper">
<BaseGraph />
</div>
</template>
<style scoped>
.graph-wrapper {
--node-color: #1890ff; /* 定义 CSS 变量 */
}
::v-deep .g6-node {
fill: var(--node-color); /* 引用变量 */
}
</style>
这样改组件内 --node-color 就能动态切换节点颜色,结合 Vue 响应式(如主题切换)很方便。
Vue3 的状态管理(Pinia/Vuex)怎么和 G6 结合?
若项目用 Pinia 或 Vuex 管理全局状态,G6 组件可无缝配合:
从状态管理取数据
比如用 Pinia 存可视化数据,组件用 useStore 获取:
import { useGraphStore } from '@/stores/graph'
const graphStore = useGraphStore()
const nodes = computed(() => graphStore.nodes) // 响应式获取节点数据
const edges = computed(() => graphStore.edges) // 响应式获取边数据
再把 nodes、edges 传给 <BaseGraph> 组件,数据变化时 Vue 自动触发 G6 更新。
交互事件触发状态变更
G6 交互事件(如点击节点)可触发 Pinia 的 action 更新全局状态:
graph.on('node:click', (e) => {
const node = e.item.getModel()
graphStore.setActiveNode(node.id) // 调用 Pinia 的 action 更新全局选中节点
})
应用状态与可视化组件互动更流畅,数据流动更清晰。
从环境搭建到组件封装,再到性能优化,Vue3 与 G6 的组合既能兼顾开发效率,又能满足复杂交互需求,把这些思路落地到项目中,就能高效实现数据可视化目标,无论是快速搭建可视化原型,还是开发复杂图分析系统,这套流程都能提供明确的实践路径~
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
code前端网



