vue.js

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript类库 > vue.js > Vue3 ECharts图表tooltip不显示

在Vue3+ECharts中图表tooltip不显示问题分析和解决方法

作者:爱看星星的猪_

文章主要描述了在Vue3中使用ECharts时遇到的tooltip不显示问题,通过分析问题根源和修改方式,最终使用markRaw()工具函数解决了问题,文章详细解释了Vue和ECharts的工作原理以及markRaw的作用,最后提供了完整的修复代码示例,需要的朋友可以参考下

一、问题复现

在开发一个展示“我行信贷五级分类变化趋势”的柱状图时,出现以下情况:

我已经设置了:

tooltip: {
  trigger: 'axis',
  confine: true,
  axisPointer: { type: 'shadow' }
}

并且数据结构也正确,为什么 tooltip 还是不显示?

二、问题所在

以下是导致问题的核心代码片段:

const myChart = ref(null);
 
const initChart = () => {
  if (!classify.value) return;
  myChart.value = echarts.init(classify.value); // ❌ 问题就在这里!
  const seriesData = [];
  // ... 后续处理数据并 setOption
};

关键错误点分析

  1. myChart 是通过 ref() 创建的响应式引用。
  2. 当执行 myChart.value = echarts.init(...) 时,ECharts 的实例被赋值给了一个 Vue 响应式对象
  3. Vue 会对这个实例进行 Proxy 包装,使其变成响应式代理对象。
  4. ECharts 内部依赖于对象的原始引用(如 === 判断、事件绑定等),一旦被 Proxy 包裹,就会导致:

所以,尽管写了正确的 tooltip.trigger: 'axis',但由于 ECharts 实例被 Vue “污染”,它根本不知道该什么时候触发提示框!

三、修改方式:使用markRaw解决

修改后的代码(关键改动)

const myChart = ref(null);
 
const initChart = () => {
  if (!classify.value) return;
  myChart.value = markRaw(echarts.init(classify.value)); // ✅ 使用 markRaw 包裹
  const seriesData = [];
  // ... 后续处理数据并 setOption
};

改动说明

原始代码修改后
myChart.value = echarts.init(...)myChart.value = markRaw(echarts.init(...))

markRaw() 是 Vue 提供的一个工具函数,作用是:标记某个值永远不要被转为响应式

四、原理详解:为什么markRaw能解决问题?

1. Vue 的响应式机制

Vue 3 使用 Proxy 实现响应式系统。当你把一个对象赋值给 refreactive 时,Vue 会创建一个代理对象来拦截读写操作。

const obj = { a: 1 }
const reactiveObj = reactive(obj) // ← 现在是 Proxy 对象

2. ECharts 的内部机制

ECharts 在初始化时会做以下事情:

如果实例被 Proxy 包裹,这些比较和绑定就会失败。

3.markRaw的作用

const rawInstance = markRaw(echarts.init(dom))

这行代码告诉 Vue:“这个对象不需要被代理,请原封不动地传下去。”

这样,ECharts 就拿到了一个原始的、未被包装的实例,可以正常运行其内部逻辑。

五、修改后的影响 & 如何应对

正面影响

优势说明
tooltip 正常显示鼠标悬停时弹出详细信息
formatter 函数生效可自定义提示内容
axisPointer 正确工作阴影指示器正常显示
✅ 图表性能提升避免不必要的响应式追踪

潜在影响与解决方案

1.不能直接在模板中绑定myChart.value

因为 myChart.valuemarkRaw 的结果,不是响应式对象,所以:

<!-- ❌ 错误:不能直接在模板中使用 -->
<template>
  <div>{{ myChart.value }}</div> <!-- 会报错或显示 undefined -->
</template>

✅ 解决方案:只在 JS 中使用,不暴露到模板。

2.无法自动响应容器大小变化

如果你希望图表随容器缩放而自动重绘,需手动监听。

✅ 解决方案:使用 ResizeObserver 手动调用 resize()

const resizeObserver = ref(null)
 
onMounted(() => {
  resizeObserver.value = new ResizeObserver(() => {
    myChart.value?.resize()
  })
  resizeObserver.value.observe(classify.value)
})
 
onUnmounted(() => {
  resizeObserver.value?.disconnect()
})

3.生命周期管理仍需注意

即使用了 markRaw,也要记得销毁图表:

onUnmounted(() => {
  myChart.value?.dispose()
})

六、完整示例代码(修复版)

<template>
  <el-card class="customer-risk-card">
    <span class="title">我行信贷五级分类变化趋势(金额:万元)</span>
    <div ref="classify" class="chart"></div>
  </el-card>
</template>
 
<script setup>
import { ref, onMounted, onUnmounted, markRaw } from 'vue'
import * as echarts from 'echarts'
import RwmscenterApi from '@api/rwmscenter/rwmscenterApi.ts'
 
const props = defineProps({
  params: Object,
})
 
const classify = ref(null)
const myChart = ref(null)
const chartData = ref({
  xAxisData: [],
  originData: {},
})
const resizeObserver = ref(null)
 
// 获取图表数据
const getCstRiskFiveGrade = async () => {
  try {
    const res = await RwmscenterApi.getCstRiskFiveGrade({
      cstGycd: props.params.cstGycd,
      cstNo: props.params.cstNo,
    })
    chartData.value.originData = res.body.data
    initChart()
  } catch (error) {
    console.error('获取数据失败:', error)
  }
}
 
// 初始化图表
const initChart = () => {
  if (!classify.value) return
  myChart.value = markRaw(echarts.init(classify.value)) // ✅ 关键:使用 markRaw
  const seriesData = []
 
  for (const key in chartData.value.originData) {
    seriesData.push({
      name: key,
      type: 'bar',
      stack: 'total',
      label: { show: false },
      emphasis: { focus: 'series' },
      data: chartData.value.originData[key].map(item => item.value / 10000),
      barWidth: 16,
    })
  }
 
  const option = {
    colors: [
      { type: 'linear', x: 0, y: 0, x2: 0, y2: 1, colorStops: [{ offset: 0, color: '#66DAA8' }, { offset: 1, color: '#fff' }] },
      { type: 'linear', x: 0, y: 0, x2: 0, y2: 1, colorStops: [{ offset: 0, color: '#A30559' }, { offset: 1, color: '#fff' }] },
      { type: 'linear', x: 0, y: 0, x2: 0, y2: 1, colorStops: [{ offset: ?('FFBE5E'), { offset: 1, color: '#fff' }] },
      { type: 'linear', x: 0, y: 0, x2: 0, y2: 1, colorStops: [{ offset: 0, color: '#FA95E' }, { offset: 1, color: '#fff' }] },
      { type: 'linear', x: 0, y: 0, x2: 0, y2: 1, colorStops: [{ offset: 0, color: '#FFBE5E' }, { offset: 1, color: '#fff' }] },
    ],
    tooltip: {
      trigger: 'axis',
      confine: true,
      axisPointer: { type: 'shadow' },
      formatter: function(params) {
        const date = params[0].axisValue
        let html = `<div style="padding: 8px; background: white; border-radius: 4px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); font-size: 12px;">`
        html += `<strong>${date}</strong>
`
        params.forEach(param => {
          html += `<span style="color: ${param.color};">●</span> ${param.seriesName}: <strong>${param.value} 万元</strong>
`
        })
        html += '</div>'
        return html
      }
    },
    legend: { right: 'center' },
    grid: { left: '3%', right: '4%', bottom: '8%', containLabel: true },
    yAxis: { type: 'value', name: '', nameTextStyle: { color: '#999999', fontSize: 12 }, axisLabel: { color: '#666666', fontSize: 12 }, splitLine: { show: true, lineStyle: { color: '#EEEEEE' } } },
    xAxis: { type: 'category', axisLine: { show: true, lineStyle: { color: '#F3F4F9' } }, axisTick: { show: false }, axisLabel: { color: '#666666', fontSize: 12 }, data: chartData.value.xAxisData },
    series: seriesData
  }
 
  myChart.value.setOption(option)
}
 
// 监听窗口大小变化
const handlerResize = () => {
  myChart.value?.resize()
}
 
onMounted(() => {
  getCstRiskFiveGrade()
  if (typeof ResizeObserver !== 'undefined') {
    resizeObserver.value = new ResizeObserver(entries => handlerResize())
    resizeObserver.value.observe(classify.value)
  }
})
 
onBeforeUnmount(() => {
  if (myChart.value) myChart.value.dispose()
  if (resizeObserver.value) resizeObserver.value.disconnect()
})
</script>
 
<style lang="less" scoped>
.customer-risk-card {
  .title {
    font-size: 14px;
    font-weight: bold;
    margin-bottom: 10px;
  }
  .chart {
    width: 100%;
    height: 300px;
  }
}
</style>

总结:解决问题的完整路径

步骤操作目的
1️⃣发现 tooltip 不显示定位问题
2️⃣查看代码发现 myChart.value = echarts.init(...)找到根源
3️⃣使用 markRaw 包裹实例防止 Vue 干扰
4️⃣添加 formatter 自定义提示实现期望效果
5️⃣补充 resizedispose完善生命周期管理

结论:当在 Vue 3 中使用 ECharts 时,若遇到 tooltip 无法触发、formatter 不执行等问题,绝大多数情况下是因为 ECharts 实例被 Vue 的响应式系统干扰了
解决方案就是:使用 markRaw(echarts.init(...)),让实例保持“原生状态”。

以上就是在Vue3+ECharts中图表tooltip不显示问题分析和解决方法的详细内容,更多关于Vue3 ECharts图表tooltip不显示的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文