从封装Vue Echarts到背后技术点

从封装一个Vue 的echart组件,到思考自己封装第三方库的技术要点和要小心的坑。

这边文章的灵感大部分来自极客时间《唐金洲 Vue开发实战》中的《如何在组件中使用ECharts、Antv等其他第三方库》。这门课真的很棒,也推荐给大家。

组件封装思路

Echarts如何使用?官方文档是这么说的:

1
2
var myChart = echarts.init(document.getElementById('main'));
myChart.setOption({xx:'xx'});

Echarts一样,很多js库,需要一个容器,比如 #app #container 等,后续js会向容器内插入DOM元素。

在jQuery时代和原生里, xxx.init(document.querySelector('#container'))。在Vue里显然不能这样了, 需要封装。那就是使用 ref

1
<div ref="chartDOM"></div>
1
init(this.$refs.chartDOM)

后续把这个组件放置到 @/components/chart.Vue内,后续引入和使用即可。

1
<my-echart style="height:400px"></my-echart>

思路就是这样。

既然是封装还要时刻警惕,是否需要使用 beforeDestory 这个生命周期函数,来把必要的数据设置为null

优化细节点

屏幕缩放

屏幕变化了,图表怎么办?这里以前做活动专题遇到过这个问题。echarts提供了resize()这个方法。那我们必要的时候使用这个方法即可。

说道屏幕滚动,屏幕尺寸变化,需要警惕,警惕什么?函数防抖,减少单位时间内触发的次数,那习惯性地要联想到 lodash 里的 _.debounce

生命周期销毁组件

这个组件会被复用,需要警惕,警惕什么?内存泄露,这个我们前端组遇到过此类问题。在合适的时候 beforeDestory清空组件,置为null

合理合并配置项

配置项比较多,可以考虑把通用参数放到组件内部,自定义参数放到外部,合并即可,这里考虑嵌套的问题,可能需要引入 _.merge 注意这里的传入参数。

1
merge({}, this.defaultChartsOption, this.options);

demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
<template>
<div ref="chart"></div>
</template>
<script lang="ts">
import echarts from 'echarts';
import {debounce, merge} from 'lodash';
import {addListener, removeListener} from 'resize-detector'; // 页面尺寸变化自动监听
import {Component, Prop, Vue, Watch} from 'Vue-property-decorator';

/**
* 封装Echarts组件库
*/

@Component
export default class EchartsComponent extends Vue {
/**
* 传入自定义配置
*/

@Prop()
public options!: any;

/**
* 默认配置项,后续可传入参数覆盖
*/

public defaultChartsOption = {
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b} : {c} ({d}%)'
},
legend: {
orient: 'vertical',
left: 'right'
},
series: [
{
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
};

/**
* 整理配置项
*/

get chartOptions() {
return merge({}, this.defaultChartsOption, this.options);
}

@Watch('options')
public optionChange() {
this.myChart.setOption(merge({}, this.defaultChartsOption, this.options));
}

/**
* Echart 容器元素
*/

public myChart = null;

// @Watch()

/**
* 绘制图表
*/

public renderChart() {
this.myChart = echarts.init(this.$refs.chart, 'light');
this.myChart.setOption(this.chartOptions);
}

/**
* resize 监听页面尺寸变化
*/

public resize() {
this.myChart.resize();
}

/**
* 页面初始化
*/

public created() {
this.resize = debounce(this.resize, 300);
}

/**
* 页面挂载
*/

public mounted() {
this.renderChart();
addListener(this.$refs.chart as HTMLElement, this.resize);
}

/**
* 页面销毁
*/

private beforeDestory() {
removeListener(this.$refs.chart as HTMLElement, this.resize);
this.myChart.dispose();
this.myChart = null;
}
}
</script>

请我喝杯咖啡吧~