项目场景:
在我们的项目架构中,集成的Echarts图表组件采用了折线图,业务需求即每300毫秒自动更新图表上的数据,并且每一次的数据点数量达到了约700个,折线图刷新的很快,每300毫秒就要刷新数据
问题描述
开发过程中发现在这种数据量请求频率下,大概2个小时左右就会导致整个页面卡死,无法操作。问题很严重
原因分析:
经过详细的排查发现是echarts图表在大数据量下确实会有这种问题。我们业务虽然只有两个图标,但更新频率很快,也遇到了此问题。原因是多方面的,一个是配置不高的电脑cpu撑不住,一个echarts也有内存问题,在大数据量频繁刷新内存和cpu撑不住,直接导致页面崩溃。经过考虑采取先不降低频率,先优化图表方式。解决这个问题需要从多方面下手
解决方案:
1.解决定时轮询稳定性:
将定时轮询单独放在一个线程中,稳定定时任务运行效率
参考代码:
// worker.js
self.addEventListener("message", function (event) {
const { task } = event.data;
console.log("worker", task);
if (task === "getChartData") {
//创建定时任务
if (!self.timerThread) {
self.timerThread = this.setInterval(async () => {
self.postMessage({});
}, 300);
}
}else if (task === "stop") {
//清除定时任务
if (self.timerThread) {
this.clearInterval(self.timerThread);
self.timerThread = undefined;
}
}
});
调用
stopComputation(){
if (this.worker != null) {
this.worker.postMessage({ task: 'stop'});
this.worker.terminate();
this.worker = null;
}
},
startTimer(){
if (this.worker != null) {
this.stopComputation()
}
this.worker = new Worker(new URL('./chartthread.js', import.meta.url));
this.worker.postMessage({ task: 'getChartData'});
this.worker.onmessage = async () => {
console.log('Worker has been getVibration...............................');
this.getVibration()
};
// 清理 Worker
this.worker.onclose = function() {
console.log('Worker has been terminated.');
};
修改之后发现发现有效果,但是效果不明显,最后还是卡死
2.将echarts实例单独提取到vue外部,避免vue多余监听浪费性能
具体代码
<script>
import elementResizeDetectorMaker from 'element-resize-detector' // 尺寸监听组件
//设置此处会被多个vue组件公用,所以用key,value方式保存各个组件页面的实例,避免echarts实例公用
let echartObject = {}
export default {
.......此处省略部分代码........
data () {
return {
options: {},
chartInstanceIndex: null, //图表实例
timer: null
}
},
mounted () {
//随机8位数
this.chartInstanceIndex = Math.random().toString(36)
this.timer = setInterval(() => {
this.initChart()
}, 60000)
this.initChart()
const erd = elementResizeDetectorMaker()
// 为了健壮性,可以考虑一下这里获取dom元素进行一个判断,如果没有获取到dom元素就不进行监听,这里就这样了
erd.listenTo(document.getElementById(this.id), (element)=> {
if (echartObject[this.chartInstanceIndex] != null && echartObject[this.chartInstanceIndex] != undefined) {
echartObject[this.chartInstanceIndex].resize()
}
})
},
methods: {
initChart() {
if(!document.getElementById(this.id)){
return
}
if (echartObject[this.chartInstanceIndex] != null && echartObject[this.chartInstanceIndex] != undefined) {
echartObject[this.chartInstanceIndex].dispose()
echartObject[this.chartInstanceIndex] == null
}
echartObject[this.chartInstanceIndex] = this.$echarts.init(document.getElementById(this.id))
this.options = {...省略....}
echartObject[this.chartInstanceIndex].setOption(this.options)
},
updateData () {
if(echartObject[this.chartInstanceIndex] != null){
echartObject[this.chartInstanceIndex].setOption({
series: [{
data: this.yData
}],
})
}
}
}
}
</script>
3.关闭动画可提高性能
官网配置项
options.animation改成false
4.配置折线图在数据量远大于像素点时候的降采样策略
官方文档:官方文档
有几种采样算法,此处我才用了第一种lttb算法。经过前面几个方法优化效果明显,但经过大约4个小时后还是撑不住,最终使用了下面的方法成功解决问题
5.定时删除并重新创建echarts实例
如果试了上面的方法还是不行。那么最后一种方式可以试一下。原理是定时重新创建并清除旧的实例,相当于重新初始化一次
代码参考:
<script>
import elementResizeDetectorMaker from 'element-resize-detector' // 尺寸监听组件
let echartObject = {}
export default {
...............................
data () {
return {
options: {},
chartInstanceIndex: null, //图表实例
timer: null
}
},
destroyed() {
if (this.timer!=null) {
clearInterval(this.timer)
this.timer == null
}
},
mounted () {
//随机8位数
this.chartInstanceIndex = Math.random().toString(36)
this.timer = setInterval(() => {
//定时重新初始化
this.initChart()
}, 60000)
this.initChart()
...........................
},
..............
methods: {
initChart() {
if(!document.getElementById(this.id)){
return
}
if (echartObject[this.chartInstanceIndex] != null && echartObject[this.chartInstanceIndex] != undefined) {
echartObject[this.chartInstanceIndex].dispose()
echartObject[this.chartInstanceIndex] == null
}
echartObject[this.chartInstanceIndex] = this.$echarts.init(document.getElementById(this.id))
this.options = {........................}
echartObject[this.chartInstanceIndex].setOption(this.options)
},
updateData () {
if(echartObject[this.chartInstanceIndex] != null){
echartObject[this.chartInstanceIndex].setOption({
series: [{
data: this.yData
}],
})
}
}
}
}
</script>
经过该方式优化后成功解决了长时间运行页面卡死的问题
总结
经过上面几个方法的优化,在没有降低请求频率的前提下成功解决页面卡死的问题。以上代码仅供参考