优化ECharts+定时器定时刷新数据导致的内存溢出问题

问题描述

项目需要使用 ECharts 绘制行车库区 3d 图,并且需要定时刷新数据:

然而数据量太大,导致页面长时间停留后会卡顿:

解决思路

  1. 不要把 ECharts 实例或者配置项挂到 vue 下面,Vue 会深度遍历监听所有属性,数据量一大就卡了
  2. 对定时器的清除
  3. 保证页面中同一个图表只有一个实例,而不是每次刷新数据都新建 ECharts 实例

具体代码

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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
<template>
<div class="chart" :style="{height:'800px',width:'100%'}" />
</template>

<script>
import echarts from 'echarts';
import 'echarts-gl';
require('echarts/theme/macarons'); // echarts theme
import resize from '@/components/Charts/mixins/resize';
import $reservoir from '@/api/reservoir'; // api

// echarts 实例, 定时器
let chart = null;
let timer = null;

export default {
mixins: [resize],
data() {
return {
playground: [] // 3d库区图数据
};
},
mounted() {
// 初始化echarts
this.initChart();
// 数据请求
this.get3DPlayground();
// 设置定时器
timer = setInterval(() => {
this.get3DPlayground();
}, 10000);
},
// 页面销毁时移除定时器,销毁echarts实例
beforeDestroy() {
if (!chart) {
return;
}
chart.dispose();
clearInterval(timer);
},
methods: {
// 获取3d图数据
async get3DPlayground() {
let data = await $reservoir.get3DPlayground();
data = data || [];
this.playground = data.map(element => {
return [
element.ypos / 1000,
element.xpos / 1000,
20 - element.zpos / 1000
];
});
data = null;
},
// 更新图表
updateChart() {
chart.setOption({
series: [
{
type: 'surface',
data: this.playground,
label: {
show: false,
position: 'top',
margin: 8
},
shading: 'realistic'
}
]
});
},
// 初始化图表
initChart() {
chart = echarts.init(this.$el, 'white', { renderer: 'canvas' });
chart.setOption({
animation: true,
animationThreshold: 2000,
animationDuration: 1000,
animationEasing: 'cubicOut',
animationDelay: 0,
animationDurationUpdate: 300,
animationEasingUpdate: 'cubicOut',
animationDelayUpdate: 0,
color: [],
series: [
{
type: 'surface',
data: [],
label: {
show: false,
position: 'top',
margin: 8
},
shading: 'realistic'
}
],
legend: [
{
data: [''],
selected: {},
show: true,
padding: 5,
itemGap: 10,
itemWidth: 25,
itemHeight: 14
}
],
tooltip: {
show: true,
trigger: 'item',
triggerOn: 'mousemove|click',
axisPointer: {
type: 'line'
},
showContent: true,
alwaysShowContent: false,
showDelay: 0,
hideDelay: 100,
textStyle: {
fontSize: 14
},
borderWidth: 0,
padding: 5
},
visualMap: {
show: true,
type: 'continuous',
min: 0,
max: 20,
inRange: {
color: ['#f05b72', '#ef5b9c', '#f47920', '#fab27b']
},
calculable: true,
inverse: false,
splitNumber: 5,
orient: 'vertical',
showLabel: true,
itemWidth: 20,
itemHeight: 150,
borderWidth: 0
},
xAxis3D: {
name: 'X',
type: 'value',
max: 'dataMax'
},
yAxis3D: {
name: 'Y',
type: 'value',
max: 'dataMax'
},
zAxis3D: {
name: 'Z',
type: 'value',
max: '20'
},
grid3D: {
boxWidth: 20,
boxHeight: 20,
boxDepth: 150,
viewControl: {
autoRotate: false,
autoRotateSpeed: 20,
rotateSensitivity: 1
}
},
title: [
{
padding: 5,
itemGap: 10
}
]
});
}
},
watch: {
playground: {
immediate: false,
deep: true,
handler(newValue, oldValue) {
// 数据更新时,更新echarts
this.updateChart();
}
}
}
};
</script>

参考