vue3的echarts图表弹框频繁点击出现白屏

本文针对ECharts在线上环境中打开图表出现白屏的问题进行了解析,并提供了有效的解决方法。问题源于ECharts在频繁初始化时产生的内存泄漏,导致系统资源耗尽。文章详细介绍了通过全局遍历创建ECharts实例及正确释放资源的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

有没有遇到过这样一个问题:在本地打开弹框的图表时,能正常打开,发布到线上时,打开过一次,第二次打开就会出现白屏现象。
原因:
echarts 频繁初始化画图时候有内存泄漏,吃掉了所有内存,就白屏画不出来了。
生成的echarts实例对象很大,占用内存过多。 echarts 里 zrender 用的canvas。
解决办法:
1.把创建的echarts变成全局遍历
2.在初始化echarts时判断
3.关闭时调用echarts.dispose(xxChart);

<template>
  <div style="width: 100%;">
    <el-dialog draggable v-model="dialogVisible" title="业绩统计" width="70%" style="min-height: 500px" :before-close="close">
      <div style="width: 100%;height: 600px; display: inline-block;" ref="orderMain"></div>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="close">取消</el-button>
        </span>
      </template>
    </el-dialog>
  </div>

</template>
const orderMain = ref(null)
var orderChart = null
function initChar() {
  // 初始化dom前调用,判断是否已存在echarts初始化的dom 
  orderChart = echarts.getInstanceByDom(orderMain.value)
  if (orderChart == null) {
    orderChart = echarts.init(orderMain.value);
  }
}
/** 关闭弹框前调用 **/
function close() {
  if (orderChart != null) {
    echarts.dispose(orderChart);
  }
  dialogVisible.value = false
}

参考:https://www.cnblogs.com/gaoyd/p/13613877.html

<think>我们正在处理用户的问题:在Vue2中使用ECharts实现内的图表自适应。用户特别提到了“”,这意味着图表可能位于一个模态(modal)或出层(popover)中,当尺寸变化或窗口尺寸变化时,图表需要自适应调整。参考引用的内容:-引用[1]和[2]提到,在窗口大小变化时,通过监听`resize`事件,调用ECharts实例的`resize`方法来实现自适应。-引用[3]提到多个图表自适应的写法,但用户可能只需要一个内的图表。-引用[4]展示了在tooltip中添加自定义按钮,但用户的需求是内的图表自适应,而不是tooltip。用户问题关键点:-环境:Vue2-需求:(modal/dialog)中的ECharts图表自适应-可能的场景:打开时图表初始化,尺寸变化(包括拖拽调整大小)时图表自适应,以及窗口大小变化时图表自适应。分析:1.内的图表自适应与普通图表自适应的区别在于,的尺寸变化可能由两种原因引起:a.浏览器窗口大小变化(windowresize)b.本身的大小变化(例如拖拽边缘调整大小)2.因此,除了监听window的resize事件,如果本身可以改变大小,我们还需要监听的尺寸变化。对于使用ElementUI等UI库的,可能提供`resize`事件;如果是原生,可能需要使用`ResizeObserver`。3.在Vue2中,我们可以使用以下步骤:a.在中放置一个容器(div)用于渲染图表。b.在打开时初始化图表(例如,在的`mounted`或`open`事件中)。c.监听窗口resize事件和的resize事件(如果有),在事件处理函数中调用图表的`resize`方法。d.在关闭时,移除事件监听,并销毁图表实例以避免内存泄漏。4.注意:如果是动态创建的(例如v-if控制),那么需要在每次打开时重新初始化图表,并在关闭时销毁。5.由于可能被拖拽调整大小,而一些UI库的在调整大小时可能不会触发window的resize事件,因此必须监听容器的大小变化。6.对于容器大小变化的监听,现代浏览器可以使用`ResizeObserver`。但需要注意兼容性(IE不支持,但Vue2项目通常需要兼容IE时,可能需要使用polyfill,或者使用其他方法)。不过,用户提到的是vue2echarts,可能已经不考虑IE(因为ECharts官方也建议在支持的环境中使用),所以我们可以使用`ResizeObserver`。7.另一种方案是,如果组件提供了resize事件(如ElementUI的Dialog有`resize`事件),则直接使用该事件。实现方案(以ElementUI的Dialog为例):1.在Dialog中放置图表容器:```html<el-dialog:visible.sync="dialogVisible"@open="initChart"@resize="handleResize"@close="destroyChart"><divid="chart-container"style="width:100%;height:400px;"></div></el-dialog>```2.在Vue组件的methods中:```javascriptmethods:{initChart(){//初始化图表this.chart=this.$echarts.init(document.getElementById(&#39;chart-container&#39;));this.chart.setOption({...});//监听窗口变化(注意:这里可能需要防抖)window.addEventListener(&#39;resize&#39;,this.handleWindowResize);//如果本身有resize事件(如ElementUI的Dialog有resize事件),则上面已经通过@resize监听了//如果没有,我们可以使用ResizeObserver监听容器变化//这里我们同时使用两种方式:窗口变化和变化(通过事件或ResizeObserver)都调用resizeChart},handleResize(){//大小变化时if(this.chart){this.chart.resize();}},handleWindowResize(){//窗口大小变化时if(this.chart){this.chart.resize();}},destroyChart(){//移除事件监听window.removeEventListener(&#39;resize&#39;,this.handleWindowResize);if(this.chart){this.chart.dispose();this.chart=null;}}}```3.使用ResizeObserver(如果组件不提供resize事件):在`initChart`中,我们可以添加对图表容器的尺寸监听:```javascriptinitChart(){//...初始化图表同上//创建ResizeObserver实例this.resizeObserver=newResizeObserver(()=>{if(this.chart){this.chart.resize();}});constchartContainer=document.getElementById(&#39;chart-container&#39;);if(chartContainer){this.resizeObserver.observe(chartContainer);}},destroyChart(){//...移除事件监听和销毁图表if(this.resizeObserver){this.resizeObserver.disconnect();this.resizeObserver=null;}}```4.注意事项:-使用`ResizeObserver`时,注意它可能会频繁触发,可以使用防抖优化。-确保在组件销毁时(beforeDestroy)也要执行destroyChart,防止内存泄漏。5.如果不是ElementUI的,而是自定义的,那么同样可以使用ResizeObserver来监听容器变化。示例代码(综合):下面提供一个使用ElementUIDialog和ECharts的完整示例。```html<template><div><el-button@click="dialogVisible=true">打开</el-button><el-dialogtitle="ECharts图表":visible.sync="dialogVisible"width="80%"@open="initChart"@close="destroyChart"@resize="handleResize"><divref="chart"style="width:100%;height:400px;"></div></el-dialog></div></template><script>import*asechartsfrom&#39;echarts&#39;;exportdefault{data(){return{dialogVisible:false,chart:null,resizeObserver:null};},methods:{initChart(){//使用$refs获取DOM元素constchartDom=this.$refs.chart;this.chart=echarts.init(chartDom);constoption={//...你的图表配置title:{text:&#39;示例图表&#39;},tooltip:{},xAxis:{type:&#39;category&#39;,data:[&#39;Mon&#39;,&#39;Tue&#39;,&#39;Wed&#39;,&#39;Thu&#39;,&#39;Fri&#39;,&#39;Sat&#39;,&#39;Sun&#39;]},yAxis:{type:&#39;value&#39;},series:[{data:[820,932,901,934,1290,1330,1320],type:&#39;line&#39;}]};this.chart.setOption(option);//监听窗口变化window.addEventListener(&#39;resize&#39;,this.handleWindowResize);//使用ResizeObserver监听容器变化(如果组件没有提供resize事件,或者为了更精确)//注意:这里我们同时使用组件的resize事件和ResizeObserver,实际上可以只选一种//由于ElementUI的Dialog有resize事件,所以我们可以不用ResizeObserver,但为了通用性,这里也加上//因为有时候内部内容变化也可能引起图表容器尺寸变化(比如动态改变内容)this.resizeObserver=newResizeObserver(()=>{this.chart&&this.chart.resize();});this.resizeObserver.observe(chartDom);},handleResize(){//大小变化时(ElementUIDialog的resize事件)this.chart&&this.chart.resize();},handleWindowResize(){//窗口大小变化时this.chart&&this.chart.resize();},destroyChart(){//移除窗口resize监听window.removeEventListener(&#39;resize&#39;,this.handleWindowResize);//停止ResizeObserver监听if(this.resizeObserver){this.resizeObserver.disconnect();this.resizeObserver=null;}//销毁图表if(this.chart){this.chart.dispose();this.chart=null;}}},beforeDestroy(){//组件销毁时也要销毁图表this.destroyChart();}};</script>```注意:-这里同时使用了窗口的resize事件和的resize事件(以及ResizeObserver)来确保图表自适应。实际上,如果的尺寸变化是由于窗口变化引起的,那么窗口resize事件会触发,同时的resize事件也会触发。所以可能触发两次resize,但ECharts的resize方法是幂等的,所以多次调用无妨。-如果担心频繁触发,可以加入防抖(例如100毫秒内只执行一次resize),但ECharts内部已经做了防抖处理,所以一般情况下不需要。相关问题:1.在Vue2中如何销毁ECharts实例?2.除了ElementUI,其他UI库的如何实现图表自适应?3.如何优化多个ECharts图表中的性能?4.在Vue2中使用ResizeObserver有哪些注意事项?</think>### Vue2 ECharts 图表自适应解决方案 在 Vue2 项目中实现ECharts 图表自适应,需同时处理 **窗口尺寸变化** 和 **展开/关闭** 两种场景。以下是完整实现方案: #### 核心实现代码 ```html <template> <!-- 组件 --> <el-dialog :visible.sync="dialogVisible" @open="initChart" @close="destroyChart" @resize="handleResize"> <!-- Element UI 的 resize 事件 --> <div ref="chartContainer" style="width:100%;height:400px;"></div> </el-dialog> </template> <script> import echarts from &#39;echarts&#39; export default { data() { return { dialogVisible: false, chartInstance: null, resizeObserver: null } }, methods: { // 初始化图表 initChart() { const dom = this.$refs.chartContainer this.chartInstance = echarts.init(dom) // 设置图表配置 this.chartInstance.setOption({ tooltip: { trigger: &#39;axis&#39; }, xAxis: { type: &#39;category&#39; }, yAxis: { type: &#39;value&#39; }, series: [{ type: &#39;line&#39;, data: [5, 20, 36, 10, 10, 20] }] }) // 监听窗口变化 window.addEventListener(&#39;resize&#39;, this.handleResize) // 监听容器尺寸变化 (现代浏览器) if (typeof ResizeObserver !== &#39;undefined&#39;) { this.resizeObserver = new ResizeObserver(() => { this.chartInstance && this.chartInstance.resize() }) this.resizeObserver.observe(dom) } }, // 处理尺寸变化 handleResize() { if (this.chartInstance) { this.chartInstance.resize() } }, // 销毁资源 destroyChart() { // 移除窗口监听 window.removeEventListener(&#39;resize&#39;, this.handleResize) // 移除容器监听 if (this.resizeObserver) { this.resizeObserver.disconnect() this.resizeObserver = null } // 销毁图表实例 if (this.chartInstance) { this.chartInstance.dispose() this.chartInstance = null } } }, beforeDestroy() { this.destroyChart() } } </script> ``` #### 关键实现点说明 1. **双重监听机制**: - `window.resize`:响应浏览器窗口变化 - `ResizeObserver`:监测容器自身尺寸变化(现代浏览器) - 对于 Element UI ,直接使用其 `@resize` 事件 2. **生命周期管理**: - 打开时初始化图表 (`@open`) - 关闭时销毁资源 (`@close`) - 组件销毁时清理监听 (`beforeDestroy`) 3. **兼容性处理**: ```javascript // 兼容旧浏览器的替代方案 if (!window.ResizeObserver) { const dom = this.$refs.chartContainer dom.__resizeTrigger__ = document.createElement(&#39;div&#39;) dom.appendChild(dom.__resizeTrigger__) dom.__resizeTrigger__.style = &#39;position:absolute;top:0;left:0;...&#39; dom.__resizeObserver__ = new MutationObserver(this.handleResize) dom.__resizeObserver__.observe(dom.__resizeTrigger__, { attributes: true, attributeFilter: [&#39;style&#39;] }) } ``` #### 内 Tooltip 优化 在中使用 tooltip 时,添加以下配置避免显示异常: ```javascript tooltip: { confine: true, // 将提示限制在图表区域内 appendToBody: false, // 避免滚动时错位 enterable: true // 允许鼠标进入提示 } ``` #### 多图表场景优化 当内有多个图表时: ```javascript handleResize() { [this.chart1, this.chart2, this.chart3].forEach(chart => { chart && chart.resize() }) } ``` [^1]: 窗口变化时通过 resize 方法实现图表自适应 [^2]: Vue 中使用 ECharts 自适应的基本解决方案 [^3]: 多个图表自适应的兼容处理方案 [^4]: 内 tooltip 的特殊配置项
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HHUFU..

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值