在项目中使用 ECharts 遇到了一些问题,包括图表不会随着窗口大小变化而变化,以及父组件向子组件传值时,ECharts 中的值不会被同步渲染等,因此写本博文进行记录;
博文中的所有代码全部收集在博主的 GitHub 仓库中,相关技术栈专栏如下:
在分析解决问题前,我们先复现一下情景。根据 ECharts 的官方文档,我们快速的在 Vue 中构建 ECharts 图表。
首先,根据官方文档的提示,在下载好的 ECharts 压缩包中提取 dist/echarts.js
放置在自己项目的目录下,并在项目中进行引用,代码如下所示:
<head>
<meta charset="UTF-8">
<title>ECharts 入门示例</title>
<script src="../../vue.js"></script>
<script src="../../echarts.js"></script>
</head>
接着,为 ECharts 准备一个 DOM 容器,代码如下所示:
<div id="main" style="width: 100%; height:400px;"></div>
最后,通过 echarts.init
方法初始化一个 ECharts 实例并通过 setOption
方法生成一个简单的柱状图,代码如下所示:
<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>
运行结果:
在上述构建的场景中,图表并不会随着窗口大小的变化而变化,如下所示:
为了实现图表的窗口自适应功能,我们需要监听窗口的大小变化,并且同时调整图表的大小,代码如下所示:
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
的防抖功能,代码如下所示:
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>
methods: {
...
resizeChart: _.debounce(function () {
console.log('chart resize!')
this.chart.resize()
}, 100)
}
运行结果:
除了使用 loadsh
的防抖功能来节约资源,提高性能之外,还可以在 Vue 实例被销毁之前,取消监控并销毁 ECharts 实例,代码如下所示:
beforeDestroy() {
if (!this.chart) {
return
}
if (this.autoResize) {
window.removeEventListener('resize', this.resizeChart)
}
this.chart.dispose()
this.chart = null
}
在前面的内容中,我们成功地开发了一个适用于特定场景的 Echarts 图表组件。为了让这个组件更具普适性和扩展性,我们计划对其进行一些改进和优化。
首先,我们将 div
元素的属性改进成动态绑定的方式,代码如下:
<div :class="className" :style="{ height: height, width: width }"></div>
然后,设置组件的 props
配置,这些 props
允许在父组件中给子组件传递数据,同时也为这些属性提供了默认值以防止属性未被传递时出现错误,代码如下:
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 图表。
接着,我们开始构建父组件,代码如下:
<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 来请求接口,代码如下所示:
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 图表并没有渲染上:
我们在子组件中也打印一下相关数据,确认父组件的数据是否传递到子组件中,代码如下所示:
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 请求返回的情况。这种情况下,父组件在 created
或 mounted
生命周期钩子函数执行时,子组件可能已经开始渲染,但是尚未接收到通过 AJAX 请求获得的数据,因此只有默认的 props
值会被子组件使用。
可以通过在父子组件中打点来得知程序的运行情况:
// 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
中的默认值,即空数组。
那如何解决这个问题呢?其实很简单,我们只要监听对应的属性即可。
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 图表窗口自适应与数据不渲染问题 的所有内容了,希望本篇博文对大家有所帮助!欢迎大家持续关注我的博客,一起分享学习和成长的乐趣!✨
代码:
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。