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

posted @ 2022-10-11 14:04  你风致  阅读(4138)  评论(2编辑  收藏  举报