vue 里主动销毁 keep-alive 缓存的组件

问题产生的背景

咱们一个后台,在切换一些标签页的时候,是使用的 keep-alive 缓存的标签页,也使用了 include 属性来决定哪一个页面进行缓存,而标签页的切换其实是路由的切换,也就是说打开一个新标签页的时候,url 会跟着变化,老的标签页若是在 keep-aliveinclude 范围内那就会缓存下来。
而后客服人员就反馈页面开的久了就会崩溃,由于他们基础上不会刷新页面(工做须要),又总有切换标签的习惯,最后致使内存愈来愈大最后崩溃。javascript

依赖环境

这个项目是基于一个开源 vue 后台框架:https://github.com/PanJiaChen/vue-element-admin,而后代码一直由几个后端开发维护的!因此后端没找出问题在哪,而后就我来接手这个问题了。
写文章时,标签里居然没有 vue 这一项,差评!vue

定位问题

先梳理下业务逻辑:从业务场景来讲,咱们在标签页之间切换时,若是刚进入的这个标签页已被缓存了,那被缓存的标签页就直接拿出来展现就行,而关闭这个标签页的时候就应该销毁对应的组件。java

clipboard.png

花了点时间查看了一下代码,发现问题在于关闭标签页的时候,虽然这个页面没在 keep-aliveinclude 里了,可是组件也没有被销毁掉,仍是在缓存状态,咱们能够经过 Vue Devtools 插件看到关闭后的标签页对应的组件一直还存在着:git

clipboard.png

固然,在这块 keep-alive 自己的逻辑我以为是没问题的,主要是咱们项目比较复杂,缓存的组件太多了并且会一直增长,因此最终致使崩溃。github

解决问题

既然问题已经定位了,那就好解决问题了,只须要在关闭标签页的时候把对应的组件也销毁掉就行了。vuex

通过网上一翻查找,发现销毁一个组件可使用: this.$destroy(‘组件名’) 来销毁。后端

先说下大概思路:keep-aliveinclude 里存的实际上是一个 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 混入到组件对象中,这样组件中要添加的代码量就比较少了。dom

更改后通过测试,关闭标签页后对应的组件就会被销毁掉,使用 Vue Devtools 能看的很清楚。

更多思考

在咱们后台操做这么频繁的业务场景下,使用 keep-alive 其实并非一个好的选择。

在咱们修复这个问题后,咱们经过控制台里的 Memory 查看页面内存的变化时,发现组件即使被销毁,也要通过一段时间才能回收完,当咱们在这一段时间一直建立/打开新的标签页时,内存仍是会在短期内高涨。并且有时候,内存在通过一段时间后也并无回收掉。

keep-alive 本质上是把整个 dom 节点及对应的事件等都缓存下来了,当这样的组件不少的时候,天然会占用不少内存。而若是咱们只缓存这个组件中的数据,在须要这个组件再次显示的时候再临时渲染那确定要节省不少内存的,毕竟数据占的空间其实很小的,而渲染组件要花的时间也不会很长(只要组件不是特别特别复杂)。

因此,下一阶段的优化工做就是把 keep-alive 去掉,而后使用 vuex 来缓存组件中的数据,当须要从新显示数据时再把数据取出来并从新渲染。固然,这是一个比较大的工程!

相关文章
相关标签/搜索