vue删除Keep-alive缓存

keep-alive简单介绍:

  • 我们项目中为了用户有更好的体验/页面性能,大多数地方都会使用到keep-alive,这个是vue提供的组件,它会将它的子组件进行数据缓存/行为缓存。
    大致原理就是当离开当前组件时,组件并不会被销毁,而是保存在内存中,下次再次访问的时候,不会重新创建新的组件实例,直接从内存中通过特定的key取出缓存的组件实例,进行渲染。

问题:
**

  • 最近我们项目提了一个用户体验的需求,希望同一个页面有些场景有缓存,但是有些场景不希望缓存。

**

需求背景:

  • 当用户将标签页关闭之后,再打开时,之前的缓存还存在用户希望当标签页存在,只是切换标签页时,希望有缓存,当用户点击关闭当前标签页时,希望将缓存清除

探究

  • 当时我一开始看到这个需求,就开始找vue的文档,但是发现vue官方文档并没有提供这样的方法,所以就索性看keep-alive源码。下面直接贴出核心设置缓存的源码,一些关键的地方我都写上了注释
export default {
  name: 'keep-alive',
  abstract: true,

  props: {
    include: patternTypes,
    exclude: patternTypes,
    max: [String, Number]
  },

  created () {
    // 初始化缓存列表,用于存储缓存的虚拟dom
    this.cache = Object.create(null)
    // 缓存的虚拟dom的key值列表
    this.keys = []
  },

  destroyed () {
    for (const key in this.cache) {
      pruneCacheEntry(this.cache, key, this.keys)
    }
  },

  mounted () {
    this.$watch('include', val => {
      pruneCache(this, name => matches(val, name))
    })
    this.$watch('exclude', val => {
      pruneCache(this, name => !matches(val, name))
    })
  },

  render () {
    const slot = this.$slots.default
    // 获取keep-alive的第一个子节点(组件),这里要注意是第一个子节点,缓存存的也是这个子节点(组件)
    const vnode: VNode = getFirstComponentChild(slot)
    // 获取第一个子节点(组件)的配置信息
    const componentOptions: ?VNodeComponentOptions = vnode && vnode.componentOptions
    if (componentOptions) {
      // check pattern
      const name: ?string = getComponentName(componentOptions)
      const { include, exclude } = this
      if (
        // not included
        (include && (!name || !matches(include, name))) ||
        // excluded
        (exclude && name && matches(exclude, name))
      ) {
        return vnode
      }

      const { cache, keys } = this
      // 这里获取第一个子节点(组件)的key,
      //如果没有key则利用ctro.cid和组件的name以及tag值拼接生成一个
      const key: ?string = vnode.key == null
        // same constructor may get registered as different local components
        // so cid alone is not enough (#3269)
        ? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '')
        : vnode.key
      // 判断是否存在缓存
      if (cache[key]) {
        // 如果已经存在,则使用缓存更新当前组件
        vnode.componentInstance = cache[key].componentInstance
        // make current key freshest
        // 同时更新Key
        remove(keys, key)
        keys.push(key)
      } else {
        // 如果不存在,则直接存入cache
        // 这里关注cache 和keys 
        // 因为我们后续需要操作这两个变量进行缓存删除
        cache[key] = vnode
        keys.push(key)
        // prune oldest entry
        if (this.max && keys.length > parseInt(this.max)) {
          pruneCacheEntry(cache, keys[0], keys, this._vnode)
        }
      }

      vnode.data.keepAlive = true
    }
    return vnode || (slot && slot[0])
  }
}

通过上述源码,我们可以知道缓存都是存在了keep-alive组件内的cache对象内,以及同时保存了一个缓存组件的key值列表。我们只需要将cache内对应的组件缓存虚拟dom删除就可以达到缓存删除的目的了

实现:

/home/list.vue
//在list组件内的 beforeRouteLeave路由守卫里面操作
beforeRouteLeave(to,from,next){
 // 拿到keep-alive的cache
 // 此处我是因为多嵌套了一层 router-view
 // 所以要向上取2层才能访问到keep-alive组件
 const cache = this.$vnode.parent.parent.componentInstance.cache
 // 拿到keep-alive的keys
 const keys = this.$vnode.parent.parent.componentInstance.keys
 // 获取keep-alive第一个子组件的key值
 // 此处我是因为多嵌套了一层 router-view 
 // 所以要多向上取一次才是keep-alive的第一层子组件 router-view
 const key = this.$vnode.parent.key == null
                                ? this.$vnode.parent.componentOptions.Ctor.cid + (this.$vnode.parent.componentOptions.tag ? `::${this.$vnode.parent.componentOptions.tag}` : '')
                                : this.$vnode.parent.key;
 // 我们的业务(判断当前所有打开的标签页是否有当前页面)
 const flag = this.$store.state.tagsView.visitedViews.find(tag => tag.name === "examManagement")
    if(!flag){
      if(keys.length) {
        let index = keys.indexOf(key)
        // 删除存在keep-alive keys列表内的组件key
        if(index > -1) keys.splice(index,1)
      }
      // 删除当前组件的缓存
      delete cache[key]
      this.$destroy(); // 缓存删除了,顺便也让当前组件销毁
    }
    next()
  },

到此就实现了Keep-alive特定场景缓存删除功能
需要注意的点就是:需要访问到keep-alive组件,因为缓存是存在keep-alive组件内的cache对象内,还有就是keep-alive缓存的是第一层子组件。所以实现时要注意组件嵌套关系。

Logo

前往低代码交流专区

更多推荐