问题背景
在vue项目中,在el-tab里使用echart,echart不能正常显示,切换tab页面,echart宽度会变成100px。
在网上找了很多资料,大多都是el-tab直接使用echart,而没有echart作为子组件导入到el-tab中的例子,所以决定写一篇博文,帮助遇到同样问题的朋友。
问题所在
之所以出现这个问题,是因为el-tab页面未显示的tab下的div此时的display为none,在点击tab切换时候,div还未渲染完成,图表在执行js初始化的时候找不到这个元素,所以自动将"100%"转成了"100",最后计算出来的图表就成了100px。
其实该问题的核心在于需要在容器生成完成后或echart初始化完成后调用resize()方法。
解决方法
在网上找了很多种解决方法,其中v-if每次都需要重新渲染不符合项目需求;:lazy=true本项目无效,以下两个方法有效。
1.nextTick 推荐
关于nextTick的介绍及用法参考这篇博文:3分钟带你了解Vue3的nextTick()_vue3 nexttick()-CSDN博客
大致意思为:它可以在数据更新后,在dom中渲染后,自动执行函数。
本项目在Chart.vue里使用了echart,然后在Tab.vue中导入Chart.vue。
实现过程涉及到父组件调用子组件方法,可参考这篇博文:
【笔记】vue3.2 父组件如何调用子组件中的方法_vue3父组件调用子组件两个函数-CSDN博客
子组件Chart.vue
Chart.vue
<template>
<el-col :span="14">
<div :id="id" :class="className" :style="{ height, width }"></div>
</el-col>
</template>
Chart.vue
<script lang="ts" setup>
const chart = ref<any>("");
function resizeChart() {
chart.value.resize();
}
//暴露子组件方法供父组件调用 重要!!!
defineExpose({
resizeChart,
});
onMounted(() => {
// 图表初始化
chart.value = markRaw(
echarts.init(document.getElementById(props.id) as HTMLDivElement)
);
chart.value.setOption(option);
// 大小自适应
window.addEventListener("resize", () => {
chart.value.resize();
});
}
</script>
父组件Tab.vue
Tab.vue
<template>
<el-tabs
type="border-card"
class="demo-tabs"
v-model="activeTab"
@tab-click="handleClick"
>
<el-tab-pane label="图1" name="1" style="width: 100%">
<Chart ref="chartRef" />
</el-tab-pane>
<el-tab-pane label="图2" name="2" style="width: 100%">
<Chart2 ref="chart2Ref" />
</el-tab-pane>
</el-tabs>
</template>
Tab.vue
<script setup lang="ts">
import Chart from "./Chart.vue";
//TAB
const activeTab = ref("1");
const ChartRef = ref();
const Chart2Ref = ref();
const handleClick = (tab) => {
debugger;
if (tab.props.name === "2") {
//关键!!!!
nextTick(() => {
ChartRef.value.resizeChart();
});
} else {
// 调用 Chart2组件的方法
nextTick(() => {
Chart2Ref.value.resizeChart();
});
}
};
</script>
亲测有效无延迟!!!
2.setTimeout延迟加载(能实现,但有延迟,不太推荐)
使用到了vuex和watch方法,可参考这篇博文:
通过vuex传递参数并触发组件中的事件_vuex触发通知-CSDN博客
想法:在用户切换el-tab页面时,调用方法resize();
父组件Tab.vue
Tab.vue
<script setup lang="ts">
import { useStore } from "vuex";
const store = useStore();
const handleClick = (tab) => {
debugger;
// 在这里提交数据到 Vuex store
store.commit("setActiveTab", tab.props.name);
}
};
</script>
子组件Chart.vue
Chart.vue
<script setup lang="ts">
watch(
() => store.state.isActiveTab,
(newVal) => {
debugger;
console.log("监听成功");
setTimeout(() => {
chart.value.resize();
}, 0); // 延迟0毫秒初始化图表
}
);
</script>
vuex
考虑到父子组件通信,用vuex也很方便,因为决定采用vuex作为中介,但watch只有在值变化时才调用事件,所以增加布尔值用于改变状态,参考博文:通过vuex传递参数并触发组件中的事件_vuex触发通知-CSDN博客
store.ts
setActiveTab(state: RootState, tab: string): void {
state.activeTab = tab;
state.isActiveTab = !state.isActiveTab; // 每次调用时更新状态
},
这个方法延时设为0,在切换tab的时候也会先是很小再变大,比较影响用户体验,所以不太推荐。
总结
问题的关键就是在容器生成完成后或图形初始化完之后,再对echart进行resize()。