el-tab组件与echarts的影响

问题:点击tab切换echarts,图表高度丢失

组件:

element ui中的el-tabecharts

实例图

此处只做说明,图表尺寸丢失 切换tab之后

报错

现象分析

貌似在echarts初始化js执行的时候,页面的尺寸还未被渲染好,所以会出现echarts内组件拿不到节点宽度和高度。

原因分析

  1. ECharts 容器的尺寸在 tab 切换时没有正确更新

    在 Vue 中,<el-tab-pane> 切换时,尚未激活的 tab 中的 DOM 元素不会渲染,导致它们的尺寸为 0。当你切换到该 tab 后,ECharts 尝试渲染时,容器的宽高可能为零,从而导致图表无法正确渲染。

  2. ECharts 没有重新调整大小

    由于 el-tabs 的懒加载特性,tab 中的内容在激活之前并不会渲染,所以 ECharts 初始化时容器的尺寸是不可用的。即使激活了对应的 tabECharts 也没有感知到容器尺寸的变化,图表无法自动适应新的容器尺寸。

参考文章

  1. github.com/apache/echa...
  2. blog.csdn.net/izengjing/a...
  3. www.cnblogs.com/smile-fanyi...

代码

  • 父组件
js 复制代码
<template>
  <div class="container">
    <el-tabs v-model="activeName" @tab-click="handleClick">
      <el-tab-pane label="类型一" name="1">
        <div class="chart-container">
          <PieChart :data="oneData" ref="onePieRef" />
          <div class="chart-box" ref="oneBarRef"></div>
        </div>
      </el-tab-pane>
      <el-tab-pane label="类型二" name="2">
        <div class="chart-container">
          <PieChart :data="twoData" ref="twoPieRef" />
          <div class="chart-box" ref="twoBarRef"></div>
        </div>
      </el-tab-pane>
    </el-tabs>
  </div>
</template>

<script setup>
...
</script>

<style lang="scss" scoped>
.container {
  margin: 10px;
  padding: 20px;
  .chart-container {
    width: 100%;
    height: 50vh;
    display: flex;
    justify-content: space-between;
    align-items: center;
  }
  .chart-box {
    width: 50%;
    height: 100%;
  }
}
</style>
  • 子组件
js 复制代码
<template>
  <div class="pie" ref="pieRef"></div>
</template>

<script setup>
...
</script>

<style lang="scss" scoped>
.pie {
  width: 50%;
  height: 100%;
}
</style>

解决方案

  1. 父组件和子组件都使用setTimeout函数延迟执行(最终解决版)
  • 父组件
js 复制代码
<script setup>
    ......
    function initChart(val) {
      ...
      setTimeout(() => {
        myChart.value.resize(); // 延迟到浏览器完成 DOM 渲染之后再调用
      }, 0);
    }
    
    // 监听 tab 的点击切换
    function handleClick(tab) {
      activeName.value = tab.paneName;
      nextTick(() => {
        // 使用 `setTimeout(..., 100)` 是为了让 DOM **彻底"稳定"**下来
        //(Element Plus 的 `<el-tab-pane>` 内部有动画渲染或延迟渲染);
        setTimeout(() => {
          initChart(activeName.value); 
          // 根据当前选中的 tab 切换更新不同的饼图
          if (activeName.value === "1" && onePieRef.value) {
            onePieRef.value.resize();
          } else if (activeName.value === "2" && twoPieRef.value) {
            twoPieRef.value.resize();
          }
        }, 100);
      });
    }
</script>
  • 子组件
js 复制代码
<script setup>
    watch(
      () => props.data,
      (newVal, oldVal) => {
        nextTick(() => {
          // DOM 渲染完再去初始化饼图
          setTimeout(() => {
            initPieChart();
          }, 100);
        });
      },
      {
        immediate: true, // 组件初始化阶段就执行一次
        deep: true,
      }
    );
    
    let myChart = null;
    function initPieChart() {
      myChart = echarts.init(pieRef.value);
      myChart.setOption(options.value, true);

      setTimeout(() => {
        myChart.resize();
      }, 0);
   }
   // 对外提供的 resize 方法
   function resize() {
      if (myChart) myChart.resize();
   }
   
   // 向父组件暴露方法
   defineExpose({
      resize,
   });
</script>
  1. 可以固定echarts容器的尺寸-(可解决,但是尺寸无法适配)
  • 父组件
js 复制代码
<template>
<div class="chart-box" ref="oneBarRef" :style={'width:500px;height:500px}'></div>
<div class="chart-box" ref="twoBarRef" :style={'width:500px;height:500px}'></div>
</template>
  1. 可以在el-tab-pane标签上添加lazy-(增加之后问题未解决,可以尝试)
  • element plus 官方文档
js 复制代码
<template> 
    <div class="container"> 
        <el-tabs v-model="activeName" @tab-click="handleClick"> 
            <el-tab-pane label="类型一" name="1" lazy> ...</el-tab-pane> 
            <el-tab-pane label="类型二" name="2" lazy>...</el-tab-pane> 
        </el-tabs> 
    </div>
</template>
  1. 可以在echarts容器上添加v-if-(增加之后问题未解决,可以尝试)
js 复制代码
<template> 
    <div class="container"> 
        <el-tabs v-model="activeName" @tab-click="handleClick"> 
            <el-tab-pane label="类型一" name="1">
                <div class="chart-container" v-if = "activeName === '1'"> // 这里
                    <PieChart :data="oneData" ref="onePieRef" /> 
                    <div class="chart-box" ref="oneBarRef"></div>
                </div>
            </el-tab-pane> 
            <el-tab-pane label="类型二" name="2">
                 <div class="chart-container" v-if = "activeName === '2'"> // 这里
                    <PieChart :data="twoData" ref="twoPieRef" /> 
                    <div class="chart-box" ref="twoBarRef"></div>
                </div>
            </el-tab-pane> 
        </el-tabs> 
    </div>
</template>
  1. js中通过父元素的宽度重新给容器赋宽度-(麻烦-未尝试,这里仅提供思路)

其他警告⚠️及解决方案

警告 xxx.vue:198 [ECharts] There is a chart instance already initialized on the dom

说明 说明在对同一个 DOM 元素重复调用了 echarts.init(domEl),而这个 DOM 上已经有一个 ECharts 实例。

根本原因 在切换 el-tab 或更新图表时,没有先调用 dispose() 就重新 init() 了,所以 ECharts 报错说该 DOM 已经被初始化过。

解决方案

js 复制代码
function initChart(val) {
  if (myChart.value) {
    myChart.value.dispose(); // 销毁图表实例
  }
  ......
}
相关推荐
hemoo4 小时前
Element UI 日期区间定制
vue.js·element
抹茶san1 天前
el-tabs频繁切换tab引发的数据渲染混淆
前端·vue.js·element
serve the people1 天前
vue中将elementUI和echarts转成pdf文件
vue.js·elementui·echarts
图表制作解说(目标1000个图表)2 天前
ECharts散点图-散点图13,附视频讲解与代码下载
echarts·统计分析·数据可视化·散点图·大屏可视化
Danny_FD2 天前
ECharts中的markPoint使用,最大值,最小值,展示label数值
前端·echarts
雪梅零落2 天前
Echart 地图放大缩小
echarts
柚子a2 天前
element-plus el-upload 因默认自动上传导致的一系列问题
vue.js·element
图表制作解说(目标1000个图表)2 天前
ECharts散点图-散点图14,附视频讲解与代码下载
echarts·统计分析·数据可视化·散点图·大屏可视化
三天不学习3 天前
基于 Vue3 + ECharts + GeoJson 实现区域地图钻取功能详解
前端·javascript·echarts·geojson·区域地图·钻地图