1. 问题表现
项目是轮播一个页面多个组件的形式来展示页面中的图表,模板。一个组件模板当中有3-4个图表,定时轮播接口。但是页面经常白屏,且占用工控机的CPU资源消耗太大,固来优化性能
2.项目自测
chrome的调试工具测试发现CPU占用特别高,了解了echarts的原理后发现是每一个图例在没有数据的时候它会创建一个定时器去渲染气泡,echarts图例是销毁了,这个echarts的实例还在内存当中,它的气泡渲染定时器也还在运行,并且定时器用的也有坑,setInterval 是一个宏任务,如果在它前面有很多任务或者某个任务等待时间较长比如网络请求报错等,那么这个定时器的执行时间和我们预定它执行的时间可能并不一致。
3.问题解决
(1)将setInterval
改成 setTimeout
递归形式,用setTimeout模拟setInterval行为,即:
mounted() {
this.getOverview();
},
methods: {
getOverview() {
this.timer = setTimeout(() => {
this.getOverview();
}, 30000);
},
},
data() {
return {
timer: null,
};
},
(2)用 svg 为图表的渲染器,官方解释是
做法:
chart = this.$echarts.init(this.$el, "null", {
renderer: "svg",
});
第一个参数为你的实例容器,第二个为主题的配置对象,第三个默认是Canvas,这里我们选择SVG渲染,因为数据不多,项目不大。
并且echarts 实例不要挂载在 vue 实例上,就是不放在data中,不然,每次构建响应式数据对象,都会耗费很多的时间和空间,而且chart实例赋值在this上。(this对象一直存在不会被回收)
(3) 清空 echart 实例
因为我是单页面一直轮播,所以一直没有去关注销毁或清空的事件,但是关乎到有时用户退出登录或者重新加载方面,突然想到还有个登录页。而且搜索相关资料发现就算组件销毁了,图表实例依然还在缓存里,前面所说的气泡渲染定时器也还在运行 。
第一种方案是在init之前先判断echarts实例是否存在在选择是否创建
if (chart == undefined) {
chart = echarts.init(document.getElementById(dom));
}
但是这个我的项目用的图表都是组件化,每个轮播组件用的图表都是组件,所以不会重复init,所以我选择在下次赋值前清空缓存(我的项目是轮播到当前组件就调用接口给图表重新赋值)
// 设置true清空echart缓存
chart.clear();
chart.setOption(options, true);
看了大部分人解决方案是beforeDestroy中释放页面的chart资源,有人用的是dispose()销毁图表的方法,可能会报相关resize方法的错,建议clear()方法
还有就是而且页面刷新页面时,并不会走vue的相关生命周期,所以也并不会销毁或清空之前的图表实例,建议监听页面刷新事件来执行方法
methods: {
chart(){
this.$destroy();
}
}
mounted(){
window.addEventListener('beforeunload', this.chart);
}
beforeDestroy() {
if (!chart) {
return;
}
// 清空 beforeunload 事件处理函数
window.removeEventListener('beforeunload', this.clearChart);
// 或者chart.clear();
chart = null;
}