Code前端首页关于Code前端联系我们

Vue3 里怎么用 G6 做可视化?从环境到复杂交互全流程拆解

terry 2小时前 阅读数 10 #SEO
文章标签 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 中,可用 reactiveref 包装数据为响应式,再传给 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(如 addNodeupdateNode),仅更改变动部分以降低渲染压力。

怎么给 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>

其他组件使用时,直接传 nodesedges 并监听事件:

<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) // 响应式获取边数据

再把 nodesedges 传给 <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前端网发表,如需转载,请注明页面地址。

热门