IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> vue中如何使用keep-alive动态删除已缓存组件 -> 正文阅读

[JavaScript知识库]vue中如何使用keep-alive动态删除已缓存组件

项目场景:

在做后台管理系统的时候,有这样一个需求:
后台的界面如下:
在这里插入图片描述

点击左边的菜单,会在右边的顶部生成一个个tag导航标签。当打开多个tag页时,用户可以在多个tag之间进行切换。需要在新增,修改页面切换tag时候,保留之前的信息,不进行页面的刷新。


问题描述

经过查询vue文档,可以使用keep-alive实现标签路由缓存,实现方式如下:
在路由配置的meta中添加keepAlive,如下:

{
    path: '/actdebt',
    component: Layout,
    redirect: '/actdebt/add',
    children: [
      {
        path: 'add',
        name: 'XXX新增配置',
        meta: {
          keepAlive: true,
        },
        component: () =>
            import (/* webpackChunkName: "page" */'@/views/bankact/actdebt/add')
      }]
  },

然后在页面中使用v-if做判断,并且加上key

<keep-alive>
   <router-view :key="$route.fullPath" class="avue-view"             
    v-if="$route.meta.keepAlive" />
</keep-alive>
   <router-view class="avue-view" v-if="!$route.meta.keepAlive" />

使用上面这种方式解决了修改不同记录的缓存问题,因为不同记录的fullPath 不一样,这样的话key就会不一样。但是对于新增和修改同一条记录还是有缓存问题。例如新增一条记录保存成功后,下次在打开新增页面,还是缓存有之前的记录。修改页面也是的,修改同一条记录保存成功后,再次打开可能还是会有之前的修改数据。

解决方案:

要解决上面这种问题我想到的解决方案为:在不同的tag导航栏切换的时候,保留缓存数据。当关闭tag导航栏或者关闭页面的时候,清除缓存。

清除缓存可以在组件里面的deactivated钩子函数调用this.$destroy();但是发现下次打开这个页面的时候,新的组件不会被缓存了。

可以利用keep-alive的include,新打开标签时,把当前组件名加入到include数组里,关闭标签时从数组中删除关闭标签的组件名就可以了。Include里面的值必须和组件的name属性保持一致,如下:
在这里插入图片描述

但是如果我同一个组件加载了两次,一个需要缓存,一个不需要缓存。但是他们的name却是一样的,还是无法解决问题。

所以是否可以重写keep-alive源码,使include可以按照路由地址匹配,而不是按照组件的name匹配。完整的代码如下:
新建keepAlive.js文件
在这里插入图片描述

/**
 * base-keep-alive 主要解决问题场景:多级路由缓存
 * 前提:保证动态路由生成的route name 值都声明了且唯一
 * 基于以上对keep-alive进行以下改造:
 *   1. 组件名称获取更改为路由名称
 *   2. cache缓存key也更改为路由名称
 *   3. pruneCache
 */
 const _toString = Object.prototype.toString
 function isRegExp(v) {
   return _toString.call(v) === '[object RegExp]'
 }
 export function remove(arr, item) {
   if (arr.length) {
     const index = arr.indexOf(item)
     if (index > -1) {
       return arr.splice(index, 1)
     }
   }
 }
 /**
  * 1. 主要更改了 name 值获取的规则
  * @param {*} opts
  */
 function getComponentName(opts) {
   // return opts && (opts.Ctor.options.name || opts.tag)
   return this.$route.path
 }
 function isDef(v) {
   return v !== undefined && v !== null
 }
 function isAsyncPlaceholder(node) {
   return node.isComment && node.asyncFactory
 }
 function getFirstComponentChild(children) {
   if (Array.isArray(children)) {
     for (let i = 0; i < children.length; i++) {
       const c = children[i]
       if (isDef(c) && (isDef(c.componentOptions) || isAsyncPlaceholder(c))) {
         return c
       }
     }
   }
 }
 function matches(pattern, name) {
   if (Array.isArray(pattern)) {
     return pattern.indexOf(name) > -1
   } else if (typeof pattern === 'string') {
     return pattern.split(',').indexOf(name) > -1
   } else if (isRegExp(pattern)) {
     return pattern.test(name)
   }
   /* istanbul ignore next */
   return false
 }
 
 function pruneCache(keepAliveInstance, filter) {
   const { cache, keys, _vnode } = keepAliveInstance
   for (const key in cache) {
     const cachedNode = cache[key]
     if (cachedNode) {
       // ------------ 3. 之前默认从router-view取储存key值, 现在改为路由name, 所以这里得改成当前key
       // const name = getComponentName.call(keepAliveInstance, cachedNode.componentOptions)
       const name = key
       if (name && !filter(name)) {
         pruneCacheEntry(cache, key, keys, _vnode)
       }
     }
   }
 }
 
 function pruneCacheEntry(
   cache,
   key,
   keys,
   current
 ) {
   const cached = cache[key]
   if (cached && (!current || cached.tag !== current.tag)) {
     cached.componentInstance.$destroy()
   }
   cache[key] = null
   remove(keys, key)
 }
 
 const patternTypes = [String, RegExp, Array]
 
 export default {
   name: 'keep-alive',
   // abstract: true,
   props: {
     include: patternTypes,
     exclude: patternTypes,
     max: [String, Number]
   },
 
   created() {
     this.cache = Object.create(null)
     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
     const vnode = getFirstComponentChild(slot)
     const componentOptions = vnode && vnode.componentOptions
     if (componentOptions) {
       // check pattern
       const name = getComponentName.call(this, componentOptions)
       // ---------- 对于没有name值得设置为路由得name, 支持vue-devtool组件名称显示
       if (!componentOptions.Ctor.options.name) {
         vnode.componentOptions.Ctor.options.name
       }
       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值, 默认从router-view设置的key中获取
       // const key = vnode.key == null
       //   ? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '')
       //   : vnode.key
 
       // ------------------- 2. 储存的key值设置为路由中得name值
       const key = name
 
       if (cache[key]) {
         vnode.componentInstance = cache[key].componentInstance
         // make current key freshest
         remove(keys, key)
         keys.push(key)
       } else {
         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])
   }
 }

然后在main.js中引入该组件,使组件可以全局使用
在这里插入图片描述
在页面直接使用BaseKeepAlive:

 <BaseKeepAlive :include="cachetags">
            <router-view :key="$route.fullPath" class="avue-view" />
          </BaseKeepAlive>

cachetags 方法就是新打开标签时,把当前组件名加入到include数组里,关闭标签时从数组中删除关闭标签,源码如下:

computed: {
    ...mapGetters(['isLock',  "tagList",'isCollapse', 'website']),
    cachetags(){
      let list=[]
      for(let item of this.tagList){
        if(!validatenull(item.keepalive)&&item.keepalive){
        list.push(item.value)
        }
      
      }
    return list.join(',')
    }
    },

方法中的tagList就是导航栏当前打开相应的tag集合如下图所示
在这里插入图片描述
参考:
https://zhuanlan.zhihu.com/p/269385782

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-06-16 21:35:35  更:2022-06-16 21:36:35 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 16:50:19-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码