Vue 中多次引用 echarts 绘图组件的问题记录

背景

这两天在 Vue 项目中,用 echarts 的热力图实现了类似 kibana 机器学习的数据预测图。区别是,笔者用了 tab 签,切换总览和具体攻击 IP 的异常情况。

实现过程中,踩了两个 echarts 的坑,这里总结下这个过程。

功能描述

访问 kibana 官方任务视图 会看到这样一个异常数据热力图:
在这里插入图片描述
笔者用 tab 签将总览和攻击信息拆开了:
在这里插入图片描述
序号 ② 区域封装了一个 Vue 组件 DataGraph.vue ,在两个 tab 签中都引用它:

<el-tabs v-model="activeName" type="card" class="y-radioClassTab">
        <el-tab-pane label="总览" name="first" lazy>
          <exception-data-graph :isAll="true" :taskInfo="taskInfo"/>
        </el-tab-pane>
        <!--配置分组的任务有此切页-->
        <el-tab-pane label="攻击IP" name="second" lazy>
          <data-graph :isAll="false" :taskInfo="taskInfo"/>
        </el-tab-pane>
</el-tabs>

DataGraph.vue 组件实现时,如果绘图区域共用一个 div ,则存在一个严重问题,就是 切换时另一个 tab 签的图是空白的,一起来分析下这个问题。

热力图区域用同一个 div 的问题

从组件概念上来看,理论上父组件引用同一个子组件两次,本质上还是不同组件,绘图 div 共用一个,应该绘制在各自的子组件区域才对呢。

总览和攻击IP 的区别是:前者没有搜索框,但是都有热力图,所以抽取公共组件。以这个思路,笔者最初是这样写的:

 <el-col class="borderLine padding4_8 marginB8">
        <div style="text-align: right;height: 17px; line-height: 17px;">
          <h6 style="float: left;margin:0;">异常数据时间线</h6>
        </div>
        <div id="anomalous" style="width: 100%;min-height: 100px;"></div>
 </el-col>

第一步,启动应用,进入“总览”,能看到热力图:
在这里插入图片描述
第二步,定位 Tab 签,看到它的元素有一个 div 的 id 为 anomalous ,第二个 tab 签的 Panel 因用了懒加载还未渲染 :
在这里插入图片描述
第三步,切换到第二个页面,结果却看到空白:
在这里插入图片描述
第四步,定位 TabPanel2 的元素信息,看到它跟 TabPanel1 有一个相同 id 的 div ,但内容为空:
在这里插入图片描述
第五步,再切回去,发现热力图发生了变化:
在这里插入图片描述
调试代码,发现切到第二个页面时,创建的 echart 实例的 id 仍然是 ec_1592993260650,就是第一个页面的那个图,所以断定渲染还是发生在第一个页面上。

一个组件内多次引用 echart 渲染错乱问题

这就令人费解了:明明引用了两次,理论上是两个子组件才对呢?

仔细看了一下绘图方法,终于明白了问题的根源:

const allAnomalousOptionCharts = echarts.init(document.getElementById(chartId));
allAnomalousOptionCharts.setOption(allAnomalousOption);

echarts 绘图时,使用的是 document.getElementById ,它在整个文档范围内找到某个 id 的元素,然后完成渲染。重复引用,整个文档中有两个 id 相同的元素,所以就渲染到了第一个 div 上去了。

解决办法

DataGraph.vue 组件用多个 div ,不同的 tab 页用不同的 div,修改实现,多加一个 div:

 // 定义元素
 <div v-if="isAll && !noData" id="anomalous1" style="width: 100%;min-height: 100px;"></div>
 <div v-if="!isAll && !noData" id="anomalous2" style="width: 100%;min-height: 325px;"></div>
 <div v-if="noData"><h3 style="text-align:center">无异常数据</h3></div>

// 绘图
let chartId = 'anomalous1';
if (!this.isAll) {
  chartId = 'anomalous2';
}
 const allAnomalousOptionCharts = echarts.init(document.getElementById(chartId));
 allAnomalousOptionCharts.setOption(allAnomalousOption);

v-show 和 v-if

另外,关于无数据和有数据时页面的效果,笔者用了一个 “无数据” 文本,并试图用 v-show,来动态控制绘图组件的显示和隐藏:无数据时隐藏绘图区域,显示“无数据”文本。

测试发现,此路不通,具体表现在:

v-show 控制 echart 的 div 的显示隐藏时,当 js 完成 echarts 的实例化后,v-show 的属性即使已经设置为 true 了 ,此时页面并没有该 div,最终导致绘图失败。

解决办法:使用 v-if 强制渲染 div ,同时调用 this.$nextTick 将绘图逻辑作为回调,以确保图形能够正确绘制。

猜你喜欢

转载自blog.csdn.net/wojiushiwo945you/article/details/106948744