最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

在Vue3+ECharts中圖表tooltip不顯示問題分析和解決方法

 更新時間:2026年02月12日 09:51:32   作者:愛看星星的豬_  
文章主要描述了在Vue3中使用ECharts時遇到的tooltip不顯示問題,通過分析問題根源和修改方式,最終使用markRaw()工具函數(shù)解決了問題,文章詳細(xì)解釋了Vue和ECharts的工作原理以及markRaw的作用,最后提供了完整的修復(fù)代碼示例,需要的朋友可以參考下

一、問題復(fù)現(xiàn)

在開發(fā)一個展示“我行信貸五級分類變化趨勢”的柱狀圖時,出現(xiàn)以下情況:

  • 左側(cè)示例圖(預(yù)期效果):鼠標(biāo)懸停在某個月份上時,會彈出包含“正常類”“關(guān)注類”“不良類”金額的詳情浮窗。
  • 右側(cè)實際圖(當(dāng)前問題):雖然圖表能正常渲染,但鼠標(biāo)懸停沒有任何反應(yīng)tooltip 完全不出現(xiàn)。

我已經(jīng)設(shè)置了:

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

并且數(shù)據(jù)結(jié)構(gòu)也正確,為什么 tooltip 還是不顯示?

二、問題所在

以下是導(dǎo)致問題的核心代碼片段:

const myChart = ref(null);
 
const initChart = () => {
  if (!classify.value) return;
  myChart.value = echarts.init(classify.value); // ? 問題就在這里!
  const seriesData = [];
  // ... 后續(xù)處理數(shù)據(jù)并 setOption
};

關(guān)鍵錯誤點分析

  1. myChart 是通過 ref() 創(chuàng)建的響應(yīng)式引用。
  2. 當(dāng)執(zhí)行 myChart.value = echarts.init(...) 時,ECharts 的實例被賦值給了一個 Vue 響應(yīng)式對象。
  3. Vue 會對這個實例進(jìn)行 Proxy 包裝,使其變成響應(yīng)式代理對象。
  4. ECharts 內(nèi)部依賴于對象的原始引用(如 === 判斷、事件綁定等),一旦被 Proxy 包裹,就會導(dǎo)致:
  • tooltip 觸發(fā)邏輯失效
  • formatter 函數(shù)不執(zhí)行
  • axisPointer 無法正確定位

所以,盡管寫了正確的 tooltip.trigger: 'axis',但由于 ECharts 實例被 Vue “污染”,它根本不知道該什么時候觸發(fā)提示框!

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

修改后的代碼(關(guān)鍵改動)

const myChart = ref(null);
 
const initChart = () => {
  if (!classify.value) return;
  myChart.value = markRaw(echarts.init(classify.value)); // ? 使用 markRaw 包裹
  const seriesData = [];
  // ... 后續(xù)處理數(shù)據(jù)并 setOption
};

改動說明

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

markRaw() 是 Vue 提供的一個工具函數(shù),作用是:標(biāo)記某個值永遠(yuǎn)不要被轉(zhuǎn)為響應(yīng)式。

四、原理詳解:為什么markRaw能解決問題?

1. Vue 的響應(yīng)式機(jī)制

Vue 3 使用 Proxy 實現(xiàn)響應(yīng)式系統(tǒng)。當(dāng)你把一個對象賦值給 refreactive 時,Vue 會創(chuàng)建一個代理對象來攔截讀寫操作。

const obj = { a: 1 }
const reactiveObj = reactive(obj) // ← 現(xiàn)在是 Proxy 對象

2. ECharts 的內(nèi)部機(jī)制

ECharts 在初始化時會做以下事情:

  • 綁定事件監(jiān)聽器(如 mouseover
  • 存儲 DOM 元素引用
  • 使用 === 比較對象是否相等(用于判斷是否需要重新渲染)
  • tooltip 中通過 instance 引用獲取配置

如果實例被 Proxy 包裹,這些比較和綁定就會失敗。

3.markRaw的作用

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

這行代碼告訴 Vue:“這個對象不需要被代理,請原封不動地傳下去。”

這樣,ECharts 就拿到了一個原始的、未被包裝的實例,可以正常運(yùn)行其內(nèi)部邏輯。

五、修改后的影響 & 如何應(yīng)對

正面影響

優(yōu)勢說明
? tooltip 正常顯示鼠標(biāo)懸停時彈出詳細(xì)信息
? formatter 函數(shù)生效可自定義提示內(nèi)容
? axisPointer 正確工作陰影指示器正常顯示
? 圖表性能提升避免不必要的響應(yīng)式追蹤

潛在影響與解決方案

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

因為 myChart.valuemarkRaw 的結(jié)果,不是響應(yīng)式對象,所以:

<!-- ? 錯誤:不能直接在模板中使用 -->
<template>
  <div>{{ myChart.value }}</div> <!-- 會報錯或顯示 undefined -->
</template>

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

2.無法自動響應(yīng)容器大小變化

如果你希望圖表隨容器縮放而自動重繪,需手動監(jiān)聽。

? 解決方案:使用 ResizeObserver 手動調(diào)用 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()
})

六、完整示例代碼(修復(fù)版)

<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)
 
// 獲取圖表數(shù)據(jù)
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('獲取數(shù)據(jù)失敗:', error)
  }
}
 
// 初始化圖表
const initChart = () => {
  if (!classify.value) return
  myChart.value = markRaw(echarts.init(classify.value)) // ? 關(guān)鍵:使用 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)
}
 
// 監(jiān)聽窗口大小變化
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>

總結(jié):解決問題的完整路徑

步驟操作目的
1??發(fā)現(xiàn) tooltip 不顯示定位問題
2??查看代碼發(fā)現(xiàn) myChart.value = echarts.init(...)找到根源
3??使用 markRaw 包裹實例防止 Vue 干擾
4??添加 formatter 自定義提示實現(xiàn)期望效果
5??補(bǔ)充 resizedispose完善生命周期管理

結(jié)論:當(dāng)在 Vue 3 中使用 ECharts 時,若遇到 tooltip 無法觸發(fā)、formatter 不執(zhí)行等問題,絕大多數(shù)情況下是因為 ECharts 實例被 Vue 的響應(yīng)式系統(tǒng)干擾了。
解決方案就是:使用 markRaw(echarts.init(...)),讓實例保持“原生狀態(tài)”。

以上就是在Vue3+ECharts中圖表tooltip不顯示問題分析和解決方法的詳細(xì)內(nèi)容,更多關(guān)于Vue3 ECharts圖表tooltip不顯示的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論

白水县| 富锦市| 鄯善县| 东方市| 延长县| 万荣县| 顺义区| 车险| 三明市| 孟津县| 徐汇区| 塔城市| 秭归县| 睢宁县| 中宁县| 南开区| 沂水县| 岱山县| 庐江县| 涪陵区| 万山特区| 永康市| 秭归县| 铜鼓县| 昆明市| 博罗县| 潍坊市| 余干县| 彩票| 竹北市| 高碑店市| 龙岩市| 关岭| 谢通门县| 新津县| 邹平县| 准格尔旗| 拉萨市| 汝城县| 高邑县| 太仓市|