vue3中tab详情页多开keepalive根据key去动态缓存与清除缓存
一. 场景
由于存在tab栏,当从查询页面点击列表进入详情时,需求是详情页都会新开一个tab,并缓存,
tab中的切换不会重新加载详情页数据,但是关闭一个详情tab,再次从查询页点击这条详情数据时,是需要重新加载的。
二. 问题产生
这就导致了一个问题,由于keepalive使用include或者exclude去动态添加取消缓存时,只会根据组件的name去进行缓存,当删除一个
详情页的缓存,其他打开的详情页也会被清除。如果不删除,点击刚才那条列表数据还是取的缓存中的值。
三. 思考
那是不是可以从根本上改变keepalive的include,exclude机制,将key作为条件去缓存而不仅限于name。
顺着这个思路,找到了一个控件:
https://github.com/meadmin-cn/meadmin-template/tree/main/src/components/meKeepAlive
这里通过修改keepalive的源码,集成了使用key的方式去缓存。且用includeKey和excludeKey代表缓存和不缓存的key。(原有的用include和exclude缓存name的也可以用)
四. 使用
1.将meKeepAlive文件下载下来,放入自己的项目中。
2.放入自己文件夹中后,会发现有一些全局变量是没有定义的,需要配置一下。
在webpack.config.js的plugins中定义: new webpack.DefinePlugin({ __SSR__: `true`, __DEV__: !isBuild ? `true` : `false`, __COMPAT__: `false`, __FEATURE_SUSPENSE__: `true`, __FEATURE_PROD_DEVTOOLS__: `false`, }) 在.eslintrc.js中定义: globals:{ '__DEV__': true, '__SSR__': true, '__FEATURE_PROD_DEVTOOLS__': true, '__FEATURE_SUSPENSE__': true, '__COMPAT__': true, }, 在外部index.d.ts文件中声明: declare let __SSR__: boolean; declare let __DEV__: boolean; declare let __FEATURE_PROD_DEVTOOLS__: boolean; declare let __FEATURE_SUSPENSE__: boolean; declare let __COMPAT__: boolean;
上述还有疑问也可以看看链接中源码的定义,是在vite中做了对应的处理。 还会有一些错误,是说某个key不在对象定义的类型中,这时就将那个对象类型声明为any就好。 如:本来会报一个ssContent不在vnode,将(vnode:any)即可 function getInnerChild(vnode: any) { return vnode.shapeFlag & ShapeFlags.SUSPENSE ? vnode.ssContent! : vnode; }
此时就能编译通过了。
3.引入组件并在路由中使用
<router-view> {({ Component }: { Component: any }) => { return ( <meKeepAlive excludeKey={this.store.exclude}> <Component key={this.$route.fullPath}></Component> </meKeepAlive> ); }} </router-view>
4.此处我是用的excludeKey去动态实现缓存的,且放在状态管理中统一管理:
state: () => { return { exclude: [] } }, mutations: { setExclude(state, name) { if (state.exclude.length == 0) { state.exclude.push(name) } else { var flag = false for (let i = 0; i < state.exclude.length; i++) { if (name == state.exclude[i]) { flag = true } } if (!flag) { state.exclude.push(name) } } }, deleteExclude(state, name) { if (state.exclude.length != 0) { var flag = false var index = 0 for (let i = 0; i < state.exclude.length; i++) { if (name == state.exclude[i]) { flag = true index = i } } if (flag) { state.exclude.splice(index, 1) } } }, }, actions: { async setExclude(context, name) { context.commit('setExclude', name); }, async deleteExclude(context, name) { context.commit('deleteExclude', name); }, }
5.触发缓存与不缓存时机,当tab页关闭的时候,添加到不缓存;当路由打开的时候,删除不缓存;
这一步可以都在你tab的组件中去做。
如下是在tab组件中: tab关闭的回调中调用将此标签的fullPath添加到不缓存: GlobalStore.dispatch(`setExclude`, fullPath); 监听路由变换,路由进入的时候删除不缓存,也就是将进入时的fullPath缓存: watch: { $route(to, from) { if (to.fullPath === from.fullPath) { return; } GlobalStore.dispatch(`deleteExclude`, to.fullPath); }, },
五. 注意
以上所有的fullPath都是定义菜单信息的时候自定义的变量,上面的key绑定你自定义的变量就行。
这样就能实现效果了(^-^)V