vue中如何使用keep-alive动态删除已缓存组件
本文小编为大家详细介绍“vue中如何使用keep-alive动态删除已缓存组件”,内容详细,步骤清晰,细节处理妥当,希望这篇“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集合如下图所示
读到这里,这篇“vue中如何使用keep-alive动态删除已缓存组件”文章已经介绍完毕,想要掌握这篇文章的知识点还需要大家自己动手实践使用过才能领会,如果想了解更多相关内容的文章,欢迎关注蜗牛博客行业资讯频道。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:niceseo99@gmail.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。
评论