Vue3 keep-alive实现通过key值来控制是否缓存页面

解决方法来自github上chenhaihong这位老师.

实现原理是在原有的组件外层包裹一个使用key值来命名的壳,成为一个新的组件.通常使用路由的完整路径作为key既能保证唯一也方便操作
废话不多说直接上代码:

// 自定义name的壳的集合

const wrapperMap = new Map();

//使用key值作为名字给即将渲染的组件包裹一层壳来保证被Vue正常缓存

const wrap = (fullPath, component) => {

        let wrapper;

        const wrapperName = fullPath; //壳组件的名字,路由的路径是唯一的

        //判断是否已经存在包裹好的组件

        if (wrapperMap.has(wrapperName)) {

          wrapper = wrapperMap.get(wrapperName);

        } else {

          //包裹组件

          wrapper = {

            name: wrapperName,

            render() {

              return h('div', component);

            },

          };

          //保存包裹后的组件

          wrapperMap.set(wrapperName, wrapper);

        }

        return h(wrapper);

      };

使用:

 <keep-alive v-if="openCache" :include="getCaches">

          <component :is="wrap(route.fullPath, Component)" :key="route.fullPath" />

 </keep-alive>

 <div v-else :key="route.name">

          <component :is="Component" :key="route.fullPath" />

 </div>

vben-admin中完整的代码:

multipleTab中的缓存逻辑需要自己修改下把原来的组件名字换成路由完整的路径

src\layouts\page

<template>

  <RouterView>

    <template #default="{ Component, route }">

      <transition

        :name="

          getTransitionName({

            route,

            openCache,

            enableTransition: getEnableTransition,

            cacheTabs: getCaches,

            def: getBasicTransition,

          })

        "

        mode="out-in"

        appear

      >

        <keep-alive v-if="openCache" :include="getCaches">

          <component :is="wrap(route.fullPath, Component)" :key="route.fullPath" />

        </keep-alive>

        <div v-else :key="route.name">

          <component :is="Component" :key="route.fullPath" />

        </div>

      </transition>

    </template>

  </RouterView>

  <FrameLayout v-if="getCanEmbedIFramePage" />

</template>

<script lang="ts">

  import { computed, defineComponent, unref } from 'vue';

  import FrameLayout from '/@/layouts/iframe/index.vue';

  import { useRootSetting } from '/@/hooks/setting/useRootSetting';

  import { useTransitionSetting } from '/@/hooks/setting/useTransitionSetting';

  import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting';

  import { getTransitionName } from './transition';

  import { useMultipleTabStore } from '/@/store/modules/multipleTab';

  import { h } from 'vue';

  // 自定义name的壳的集合

  const wrapperMap = new Map();

  export default defineComponent({

    name: 'PageLayout',

    components: { FrameLayout },

    setup() {

      const { getShowMultipleTab } = useMultipleTabSetting();

      const tabStore = useMultipleTabStore();

      const { getOpenKeepAlive, getCanEmbedIFramePage } = useRootSetting();

      const { getBasicTransition, getEnableTransition } = useTransitionSetting();

      const openCache = computed(() => unref(getOpenKeepAlive) && unref(getShowMultipleTab));

      const getCaches = computed((): string[] => {

        if (!unref(getOpenKeepAlive)) {

          return [];

        }

        return tabStore.getCachedTabList;

      });

      //使用key值作为名字给即将渲染的组件包裹一层壳来保证被Vue正常缓存

      const wrap = (fullPath, component) => {

        let wrapper;

        const wrapperName = fullPath; //壳组件的名字

        //判断是否已经存在包裹好的组件

        if (wrapperMap.has(wrapperName)) {

          wrapper = wrapperMap.get(wrapperName);

        } else {

          //包裹组件

          wrapper = {

            name: wrapperName,

            render() {

              return h('div', component);

            },

          };

          //保存包裹后的组件

          wrapperMap.set(wrapperName, wrapper);

        }

        return h(wrapper);

      };

      return {

        getTransitionName,

        openCache,

        getEnableTransition,

        getBasicTransition,

        getCaches,

        getCanEmbedIFramePage,

        wrap,

      };

    },

  });

</script>

最后再说下为什么要做这些操作:

在实际开发中会用到动态路由,也就是一个组件根据参数不同显示成不同的页面,vue现在的缓存机制是根据组件名称来实现的,一旦这个组件其中一个页面关闭触发缓存的清理机制就会导致这个组件其他的实例一起被清理掉.

上述操作就是为了实现 清除/添加 组件下的某一个实例的缓存记录

三年又三年,很多人都为vue提出了这个问题的解决方案,但是vue官方一直没有任何作为,实在不知道是什么原因阻碍了它被解决.

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值