问题介绍
使用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 |
|
shallowReactive |
|
ref |
|
shallowRef |
|
总结:
reactive
和ref
都提供深度响应性,但ref
还提供了一层额外的封装。shallowReactive
和shallowRef
仅提供一层响应性,这对于性能敏感的应用或不需要深度响应性的数据结构很有用。
修改后的实现效果
修改后的代码
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>