问题介绍
在vue开发中的时候,由于项目需要,在不同路由的页面使用了大量echarts,由于展示页面用的机子太垃圾,只能给每个路由都加上keep-alive缓存,来获得可怜的流畅度。
<template>
<el-container class="home">
<el-header>
<keep-alive>
<selfHeader></selfHeader>
</keep-alive>
</el-header>
<el-main>
<transition :name="transitionName">
<keep-alive>
<router-view></router-view>
</keep-alive>
</transition>
</el-main>
</el-container>
</template>
同时,我们为每个echarts都加上了自适应窗口大小的功能。
cityComplementBar.setOption(option)
window.addEventListener('resize', () => {
cityComplementBar.resize()
})
重点来了,在这个时候,打开咋一看没啥问题,但是当你将缓存好的页面切换到后台时(即处于disactivated状态),如果改变当前页面窗口大小,那么bug就来了。之前缓存好的页面再打开时,echarts就没了。。。就没了。。。看一下后台,还疯狂报错,咋回事呢?
分析一波,之前为了实现echarts的自适应,添加了一个监听器监测window窗口是否发生变化,如果变化了就执行echarts自带的resize方法来改变一下echarts的大小,并且将这个方法习惯性地写在了生成echarts的方法中。但是,我们加入缓存后情况就有点不一样了。我们绘制Echarts的方法是写在mounted生命周期中的,而切换页面并会不会进入destroyed生命周期,而是进入disactivated生命周期,这使得mounted中的函数还是激活状态,我们之前添加的监听器仍然在工作,但是但是但是,之前缓存页面的dom没了啊,我们的eventListener监听到了别人家的dom变了,于是想重画一下自家的echarts,但是你连画板都没有啊,画啥?于是echarts-gl开始发出上文提到的警告。echarts-gl源文件中存在这一段代码(报错的根源):
if (!zr.getWidth() || !zr.getHeight()) {
console.warn('Dom has no width or height');
return;
}
然后,当你切换回已缓存的页面时,bug就来了。echarts消失了。
然后你再改变一下宽口大小,echarts又回来了。。。
这是因为你之前你改变dom的时候,echarts重画失败了,可是你切回来的时候,dom虽然相对于你切出去之前可能变了,但是对于监听器来说,他只关注于当前dom大小是否改变了。所以刚切回来的时候echarts的resize方法不会被激活。而只要稍稍改变一下dom的大小,就可以激活resize方法。
解决办法
咋办呢?我用的方法其实挺简单的。首先,在data中注册一个全局的变量。
data() {
return { cityComplementBar: null }
},
然后,把dom中用于绘制echarts的id赋值到注册的全局变量中
this.cityComplementBar = echarts.init(document.getElementById('cityComplementBar'));
随后,不管是注册echarts还是setOption,或者resize都在这个全局变量上执行就行了。特别注意,监听dom的监听器如果要拿出来单独写一个函数,必须要添加在mounted中才能使用,放在activated中就失效了。
接着,就可以在activated生命周期中单独给一个resize方法,不管他窗口大小是否变化了,都resize一次。
mounted() {
this.getData()
window.addEventListener('resize', () => {
this.cityComplementBar.resize()
})
},
activated() {
if (this.cityComplementBar) {
this.cityComplementBar.resize()
}
},
最后
如果不想改源文件的话可以在deactivated生命周期取消window的尺寸监听器,每次在activated的时候都添加一个监听器就行,但是注意一定要执行一次resize,不然你中途变了窗口大小监听器可没监听到,会bug的。初次总结,欢迎指教,不足之处,请多包容。谢谢~