【问题解决】解决 ECharts 图表窗口自适应与数据不渲染问题

前言

在项目中使用 ECharts 遇到了一些问题,包括图表不会随着窗口大小变化而变化,以及父组件向子组件传值时,ECharts 中的值不会被同步渲染等,因此写本博文进行记录;

博文中的所有代码全部收集在博主的 GitHub 仓库中,相关技术栈专栏如下:

快速上手 ECharts

在分析解决问题前,我们先复现一下情景。根据 ECharts 的官方文档,我们快速的在 Vue 中构建 ECharts 图表。

首先,根据官方文档的提示,在下载好的 ECharts 压缩包中提取 dist/echarts.js 放置在自己项目的目录下,并在项目中进行引用,代码如下所示:

html 复制代码
<head>
    <meta charset="UTF-8">
    <title>ECharts 入门示例</title>
    <script src="../../vue.js"></script>
    <script src="../../echarts.js"></script>
</head>

接着,为 ECharts 准备一个 DOM 容器,代码如下所示:

html 复制代码
<div id="main" style="width: 100%; height:400px;"></div>

最后,通过 echarts.init 方法初始化一个 ECharts 实例并通过 setOption 方法生成一个简单的柱状图,代码如下所示:

html 复制代码
<script>
    const app = new Vue({
        el: '#app',
        data: {
            option: {
                title: {
                    text: 'ECharts 入门示例'
                },
                tooltip: {},
                legend: {
                    data: ['销量']
                },
                xAxis: {
                    data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
                },
                yAxis: {},
                series: [
                    {
                        name: '销量',
                        type: 'bar',
                        data: [5, 20, 36, 10, 10, 20]
                    }
                ]
            }
        },
        mounted() {
            this.initChart()
        },
        methods: {
            initChart() {
                let myChart = echarts.init(document.getElementById('main'));
                myChart.setOption(this.option);
            }
        }
    })
</script>

运行结果:

【ECharts 入门示例】代码点击此处跳转

图表自适应

在上述构建的场景中,图表并不会随着窗口大小的变化而变化,如下所示:

为了实现图表的窗口自适应功能,我们需要监听窗口的大小变化,并且同时调整图表的大小,代码如下所示:

js 复制代码
mounted() {
    this.initChart()
    if (this.autoResize) {
        window.addEventListener('resize', this.resizeChart)
    }
},
methods: {
    ...
    resizeChart() {
        console.log('chart resize!')
        this.chart.resize()
    }
}

在上述代码中,autoResize 表示是否启动自适应功能,window.addEventListener('resize', this.resizeChart) 监听窗口的大小变化,如果窗口大小发生改变,则调用 resizeChart() 方法,resizeChart() 方法中使用了 ECharts 自带的调整图表大小的方法 resize()

运行结果:

不过眼尖的读者已经发现了,只是缩小了一点窗口的大小,该方法就被调用了85次,这对我们来说是没有必要的,因为我们不需要实时的去调整窗口大小,只需要在一定时间内完成调整即可,因此引入 loadsh 的防抖功能,代码如下所示:

html 复制代码
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>
js 复制代码
methods: {
    ...
    resizeChart: _.debounce(function () {
        console.log('chart resize!')
        this.chart.resize()
    }, 100)
}

运行结果:

除了使用 loadsh 的防抖功能来节约资源,提高性能之外,还可以在 Vue 实例被销毁之前,取消监控并销毁 ECharts 实例,代码如下所示:

js 复制代码
beforeDestroy() {
    if (!this.chart) {
        return
    }
    if (this.autoResize) {
        window.removeEventListener('resize', this.resizeChart)
    }
    this.chart.dispose()
    this.chart = null
}

【ECharts 图表自适应】代码点击此处跳转

数据不渲染

在前面的内容中,我们成功地开发了一个适用于特定场景的 Echarts 图表组件。为了让这个组件更具普适性和扩展性,我们计划对其进行一些改进和优化。

首先,我们将 div 元素的属性改进成动态绑定的方式,代码如下:

html 复制代码
<div :class="className" :style="{ height: height, width: width }"></div>

然后,设置组件的 props 配置,这些 props 允许在父组件中给子组件传递数据,同时也为这些属性提供了默认值以防止属性未被传递时出现错误,代码如下:

js 复制代码
props: {
  className: {
    type: String,
    default: "DataChild",
  },
  width: {
    type: String,
    default: "100%",
  },
  height: {
    type: String,
    default: "500px",
  },
  autoResize: {
    type: Boolean,
    default: true,
  },
  series: {
    type: Array,
    default: null,
  },
},

在上述代码中,父组件传入不同的 series 数值,可以动态的改变 ECharts 图表。

接着,我们开始构建父组件,代码如下:

html 复制代码
<template>
  <data-child :series="this.series"></data-child>
</template>

<script>
import DataChild from "./DataChild.vue";

export default {
  name: "DataParent",
  components: {
    DataChild,
  },
  data() {
    return {
      series: [
        {
          name: "..",
          type: "..",
          data: [],
        },
      ],
    };
  },
  created() {
    this.fetchData();
  },
  methods: {
    fetchData() {
      ...
    },
  },
};
</script>

在上述代码中,通过 fetchData() 方法来获取需要渲染数据,这里的话使用 POSTMAN 来模拟后端服务器发送数据。

创建一个模拟服务器,设置 API 接口与响应数据:

对模拟服务器进行相关配置:

通过访问 API 接口来获取数据:

同时也能查看到请求日志:

在 Vue 中,我们通过 axios 来请求接口,代码如下所示:

js 复制代码
fetchData() {
  const url = "";
  axios
    .get(url + "/test/data")
    .then((resp) => {
      this.series[0].data = resp.data.data;
      console.log(this.series);
    })
    .catch((err) => {
      console.log(err);
    });
},

然而出现了点问题,数据是请求到了,但是 ECharts 图表并没有渲染上:

我们在子组件中也打印一下相关数据,确认父组件的数据是否传递到子组件中,代码如下所示:

js 复制代码
console.log('Child Data Before:', this.series)
this.initChart();
console.log('Child Data After:', this.series)

initChart() {
  ...
  console.log('Child Data:', this.series)
  this.chart.setOption(this.option);
},

子组件也确实获取到了数据,那为什么图表不渲染数据呢?

这是因为,当父组件需要通过异步 AJAX 请求获取数据来设置子组件的 props 属性时,可能会遇到子组件渲染速度快于 AJAX 请求返回的情况。这种情况下,父组件在 createdmounted 生命周期钩子函数执行时,子组件可能已经开始渲染,但是尚未接收到通过 AJAX 请求获得的数据,因此只有默认的 props 值会被子组件使用。

可以通过在父子组件中打点来得知程序的运行情况:

js 复制代码
// parent
created() {
  console.log("Parent Created Before");
  this.fetchData();
  console.log("Parent Created After");
},
fetchData() {
  axios
    ...
    .then((resp) => {
      ...
      console.log("Parent Fetch Data");
    })
    ...
},

// child
created() {
  console.log("Child Created");
},
mounted() {
  console.log("Child Mounted Before");
  this.initChart();
  console.log("Child Mounted After");
  ...
},
initChart() {
  ...
  console.log('Child Init Data')
  ...
},

运行结果:

通过上述的运行结果可知,正如我们所预料的那样,由于父组件的 fetchData() 方法使用了异步请求,因此程序会一直执行下去,并不会因为没有获取到数据而阻塞,当子组件完成图表渲染 initChart() 后,父组件才请求到数据,因此才造成了子组件图表中的数据不渲染问题 ,子组件图表渲染时使用的数据是 props 中的默认值,即空数组。

那如何解决这个问题呢?其实很简单,我们只要监听对应的属性即可。

js 复制代码
watch: {
  option: {
    handler(newVal, oldVal) {
      if (this.chart) {
        if (newVal) {
          this.chart.setOption(newVal);
        } else {
          this.chart.setOption(oldVal);
        }
      } else {
        this.initChart();
      }
    },
    deep: true,
  },
},

上述代码之所以监听的是 option 而不是 series,是因为在初始化图表时已经进行了赋值 this.option.series = this.series;series 本身是 option 的属性,通过 deep: true 也可以监听到 series 发生变化,同时监听 option 还能监听到其他属性。

运行结果:

【ECharts 数据不渲染】代码点击此处跳转

后记

以上就是 解决 ECharts 图表窗口自适应与数据不渲染问题 的所有内容了,希望本篇博文对大家有所帮助!欢迎大家持续关注我的博客,一起分享学习和成长的乐趣!✨

代码:

📝 上篇精讲:解决 swagger2 默认地址失效

💖 我是 𝓼𝓲𝓭𝓲𝓸𝓽,期待你的关注,创作不易,请多多支持;

👍 公众号:sidiot的技术驿站

🔥 系列专栏:问题解决前端大杂烩Vue.js 打怪升级之路

相关推荐
崔庆才丨静觅6 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60616 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了6 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅6 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅7 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅7 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment7 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅8 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊8 小时前
jwt介绍
前端
yunteng5218 小时前
通用架构(同城双活)(单点接入)
架构·同城双活·单点接入