问题产生的背景
我们一个后台,在切换一些标签页的时候,是使用的 keep-alive 缓存的标签页,也使用了 include 属性来决定哪个页面进行缓存,而标签页的切换实际上是路由的切换,也就是说打开一个新标签页的时候,url 会跟着变化,老的标签页如果在 keep-alive 的 include 范围内那就会缓存下来。
然后客服人员就反馈页面开的久了就会崩溃,因为他们基础上不会刷新页面(工作需要),又总有切换标签的习惯,最后导致内存越来越大最后崩溃。
依赖环境
这个项目是基于一个开源 vue 后台框架:https://github.com/PanJiaChen/vue-element-admin,然后代码一直由几个后端开发维护的!所以后端没找出问题在哪,然后就我来接手这个问题了。
写文章时,标签里竟然没有 vue 这一项,差评!
定位问题
先梳理下业务逻辑:从业务场景来说,我们在标签页之间切换时,如果刚进入的这个标签页已被缓存了,那被缓存的标签页就直接拿出来展示就行,而关闭这个标签页的时候就应该销毁对应的组件。
花了点时间查看了一下代码,发现问题在于关闭标签页的时候,虽然这个页面没在 keep-alive 的 include 里了,但是组件也没有被销毁掉,还是在缓存状态,我们可以通过 Vue Devtools 插件看到关闭后的标签页对应的组件一直还存在着:
当然,在这块 keep-alive 本身的逻辑我觉得是没问题的,主要是我们项目比较复杂,缓存的组件太多了而且会一直增加,所以最终导致崩溃。
解决问题
既然问题已经定位了,那就好解决问题了,只需要在关闭标签页的时候把对应的组件也销毁掉就好了。
经过网上一翻查找,发现销毁一个组件可以使用: this.$destroy(‘组件名') 来销毁。
先说下大概思路:keep-alive 的 include 里存的其实是一个 vuex 中的一个数据源(数据源保存的是路由名称),当关闭标签页时,这个数据源中的一项会被移除。这之前,我们在组件里监听到这个数据源的变化,如果此组件对应的路由(这个路由应在 mounted 的时候保存下来)已经不在数据源中了,那就应该销毁此组件。
代码大概如下:
const mixin = {
data() {
return {
routePath: ''
}
},
mounted() {
this.routePath = this.$route.path
},
computed: {
visitedViews() {
return this.$store.state.tagsView.visitedViews
}
},
watch: {
visitedViews(value) {
if(value 里没有了 routePath 这一项)
this.$destroy(this.$options.name)
}
}
}
}
export default mixin
这一段代码单独拎出来了,然后在需要缓存的组件里使用 mixins 混入到组件对象中,这样组件中要添加的代码量就比较少了。
更改后经过测试,关闭标签页后对应的组件就会被销毁掉,使用 Vue Devtools 能看的很清楚。
更多思考
在我们后台操作这么频繁的业务场景下,使用 keep-alive 其实并不是一个好的选择。
在我们修复这个问题后,我们通过控制台里的 Memory 查看页面内存的变化时,发现组件即便被销毁,也要经过一段时间才能回收完,当我们在这一段时间一直创建/打开新的标签页时,内存还是会在短时间内高涨。而且有时候,内存在经过一段时间后也并没有回收掉。
keep-alive 本质上是把整个 dom 节点及对应的事件等都缓存下来了,当这样的组件很多的时候,自然会占用很多内存。而如果我们只缓存这个组件中的数据,在需要这个组件再次显示的时候再临时渲染那肯定要节省很多内存的,毕竟数据占的空间其实很小的,而渲染组件要花的时间也不会很长(只要组件不是特别特别复杂)。
所以,下一阶段的优化工作就是把 keep-alive 去掉,然后使用 vuex 来缓存组件中的数据,当需要重新显示数据时再把数据取出来并重新渲染。当然,这是一个比较大的工程!
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。