Vue3 + Echarts堆叠折线图的tooltip不显示问题

问题介绍

使用Echarts在Vue3+Vite项目中绘制堆叠折线图的的时候,tooltip总是不显示,经过很长时间的排查和修改,最后发现是在使用上有错误导致的。

错误图片展示

问题原因

由于Vue3底层使用proxy代理创建示例,使用其创建出来的实例与Echarts真正使用的的实例存在兼容性问题,所以Echarts无法从中获取内部变量,所以在使用Echarts实例的时候,不要使用ref或reactive等响应式方法创建Echarts对象,应该使用shallowReactive、shallowRef或者普通变量。

导致问题代码

const chartInstance3 = ref(null); //定义店铺入驻明细图表实例化
const chartContainer3 = ref();

shallowReactive、shallowRef与reactive、ref的区别

API介绍
reactive
  • reactive用于将一个对象变成响应式的。它会深度遍历对象的属性,使其内部的所有属性也变为响应式的。因此,当你修改任何层级的属性时,都会触发依赖这些属性的组件的更新。
  • 它适用于那些结构复杂并且需要整个对象及其所有属性都是响应式的情况。
shallowReactive
  • shallowReactive同样用于将对象变成响应式的,但是它只会使对象的顶层属性响应式,而不会深入到对象的嵌套属性中。这意味着如果你有一个嵌套的对象,其内部的属性更改将不会触发响应性更新。
  • 这个API对于处理大型数据结构特别有用,因为它避免了深度遍历带来的性能开销。
ref
  • ref用于创建一个响应式的引用类型,它包装了一个基本类型值或一个复杂的类型。ref的值可以通过.value属性访问和修改。
  • ref可以用于任何数据类型,但是当它包装复杂类型(如对象或数组)时,整个ref对象本身仍然是响应式的,内部的复杂类型也会被转化为响应式类型(通过reactive)。
  • ref主要用于状态管理,尤其是那些需要在组件之间共享的状态。
shallowRef
  • shallowRefref相似,但它不会将内部的复杂类型转化为响应式类型。这意味着如果shallowRef的值是一个对象,那么这个对象的内部属性更改将不会触发响应性更新。
  • 这种行为在处理大型数据结构或外部库提供的对象时非常有用,因为它们可能不希望被Vue的响应系统所影响。

总结:

  • reactiveref都提供深度响应性,但ref还提供了一层额外的封装。
  • shallowReactiveshallowRef仅提供一层响应性,这对于性能敏感的应用或不需要深度响应性的数据结构很有用。

修改后的实现效果

修改后的代码

const chartInstance3 = shallowRef(null); //定义店铺入驻明细图表实例化
const chartContainer3 = shallowRef();

附完整代码

<template>
  <div
    ref="chartContainer3"
    style="width: 100%; height: 400px; margin-top: 20px"
  ></div>
</template>

<script setup>
import * as echarts from "echarts";
import { onMounted, onUnmounted, ref, shallowRef } from "vue";
import request from "@/utils/request";
import api from "@/api";
import formatTime from "@/utils/formatTime";
const chartInstance3 = shallowRef(null); //定义店铺入驻明细图表实例化
const chartContainer3 = shallowRef();

const ServiceCategoryList = ref([]); //店铺类别列表
// 获取所有店铺类别
const getShopCategory = async () => {
  const res = await request.get(api.getShopCategory);
  res.data.forEach((item) => {
    ServiceCategoryList.value.push({
      name: item.name,
      type: "line", //图标的类型,line:折线图
      // stack: "total",
      shopType: item.type,
      smooth: "true",
      data: [],
    });
  });
};
const shopList = ref([]); //入驻店铺列表
const startTime = ref(""); //入住店铺最早的创建时间
const endTime = new Date(); //当前时间
const dateArray = ref([]); //日期数组
// 创建时间列表
const createTimeList = (start, end) => {
  while (start <= end) {
    dateArray.value.push(start.toISOString().substring(0, 10)); // 只保留YYYY-MM-DD格式
    start.setDate(start.getDate() + 1); // 增加一天
  }
};
// 获取入驻平台的所有店铺
const getShopList = async () => {
  await request.get(api.shopList).then((res) => {
    res.data.forEach((item) => {
      item.createTime = formatTime(item.createTime).nohour;
      item.createTimeObj = new Date(item.createTime);
      shopList.value.push({
        shopName: item.shopName,
        createTime: item.createTime,
        shopType: item.shopType,
      });
    });
    // 使用sort方法对shopList按创建时间排序,并找出最早入驻的时间
    res.data.sort((a, b) => a.createTimeObj - b.createTimeObj);
    startTime.value = new Date(res.data[0].createTime);
    createTimeList(startTime.value, endTime); //生成时间列表
    // 将所有的店铺按照店铺类别进行分组
    const shopsGroupedByTypeAndTime = res.data.reduce((groups, shop) => {
      const shopType = shop.shopType;
      const shopTime = shop.createTime;
      // 如果还没有对应店铺类型的分组,则创建它
      if (!groups[shopType]) {
        groups[shopType] = {};
      }
      // 如果还没有对应时间的分组,则创建它
      if (!groups[shopType][shopTime]) {
        groups[shopType][shopTime] = 0;
      }
      // 将对应时间和类型的计数增加1
      groups[shopType][shopTime]++;
      return groups;
    }, {});
    ServiceCategoryList.value.forEach((item) => {
      if (shopsGroupedByTypeAndTime[item.shopType]) {
        item.data = shopsGroupedByTypeAndTime[item.shopType];
      }
      dateArray.value.forEach((date) => {
        if (!item.data[date]) {
          item.data[date] = 0;
        }
      });
      item.data = Object.entries(item.data)
        .sort(([keyA], [keyB]) => new Date(keyA) - new Date(keyB))
        .map(([key, value]) => value);
    });
  });
};
const setOptions3 = () => {
  chartInstance3.value.setOption({
    title: {
      text: "商家入驻数据明细",
    },
    tooltip: {
      trigger: "axis",
      axisPointer: {
        type: "cross",
      },
    },
    legend: {
      data: ServiceCategoryList.value.map((item) => item.name),
    },
    grid: {
      left: "3%",
      right: "4%",
      bottom: "3%",
      containLabel: true,
    },
    xAxis: {
      type: "category",
      boundaryGap: false,
      data: dateArray.value,
    },
    yAxis: {
      type: "value",
    },
    series: [...ServiceCategoryList.value],
  });
};
onMounted(async () => {
  await getShopCategory();
  await getShopList();
  chartInstance3.value = echarts.init(chartContainer3.value);
  setOptions3();
});
onUnmounted(() => {
  // 组件卸载时销毁图表实例
  chartInstance3.value.dispose();
});
</script>

  • 8
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值