Vue 使用 <keep-alive include> 实现多级 <router-view> 缓存,无限层次缓存

本文介绍使用Vue.js中的<keep-alive>组件进行缓存的两种方法,着重讲解了利用include属性结合Vuex动态管理缓存列表的方式,并解决了嵌套路由下的组件缓存问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

keepAlive

缓存展示:
在这里插入图片描述

使用 <keep-alive> 实现缓存目前主流的有两种方法

方法一:

  1. 在路由元信息中添加缓存标识:
{
  path: 'json',
  name: 'json',
  meta: {
    ...
    keepAlive: true
    ...
  },
  component: () => import('../views/components/json')
},
  1. 使用 v-if 判断是否缓存
<keep-alive>
   <router-view v-if="$route.meta.keepAlive"/>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"/>

这种方法是比较早版本时使用的方法,当时<keep-alive>还没有include属性。这个方法虽然方便,但是会带来很多bug,比如无法使用<transition>、缓存了不必要的,用来实现嵌套路由操作的<router-view>布局组件等。

然鹅,我有强迫症,不能接受没有动效 ( ̄(00) ̄)

因此这里重点讲方法二(正在使用的):

1.将路由元信息中包含keepAlive: true的路由记录下来,并将该路由的name属性为维护在使用vuex中的一个keepAliveList: []里。

2.使用<keep-alive>include属性,来实现动态的组件缓存。

先说一下include属性,它的值可以是:字符串,正则表达式,数组

首先我们需要知道keep-alive可以根据include中的值来匹配当前路由对应组件的name属性(!!不是路由的name哦,是组件的name),来判断这个路由中的组件是否需要缓存。因此我们只需要将keepAliveList: []里保存的需要缓存的路由组件name数组传入include即可

因此使用起来就像这样

<keep-alive :include="keepAliveList">
  <router-view :key="key"/>
</keep-alive>

<script>
export default {
  name: 'index',
  data () {
    return {
      keepAliveList: this.$store.getters.getKeepAliveList,
    }
  },
  computed: {
    // 获取缓存列表
    getKeepAliveList () {
      return this.$store.getters.getKeepAliveList
    },
    key () {
      return this.$route.path
    }
  },
  watch: {
    getKeepAliveList (n, o) {
      this.keepAliveList = n
    }
  }
}
</script>

但是

如果遇到嵌套的<router-view>或者嵌套路由(这是很常见的操作),这个时候后面几层<router-view>中的组件缓存会出问题

比如我有下面的三层结构:

 {
    path: '/menu-1',
    name: 'menu-1',
    // 布局文件 ,用来实现多层嵌套的 组件访问,对于多层次的路由访问来说,这是必须的
    component: layout,    
    children: [
      {
        path: 'menu-2',
        name: 'menu-2',
        component: layout,
        children: [
        {
          path: 'menu-3',
          name: 'menu-3',
          meta: {
            keepAlive: true
        },
        component: () => import('../views/components/menu-3.vue')
        }
      ]
    }
  ]
}

文件内容 layout.vue

<template>
   <router-view/>
</template>

<script>
export default {
  name: 'layout'
}
</script>

我想要访问menu-3,在路由之中可视化可以这么看 ( layout 写错了,淦 ):
在这里插入图片描述
我们可以发现,keep-alive只缓存到第一层,也就是<layout/>这个组件,而这个组件只是一个<router-view/>组件,这明显不是我们想要的。

我们需要把<layout/>这个无用的组件从<keep-alive> <router-view/> <keep-alive/>之中提出,
换句话说是将<menu-3/>提升到<keep-alive>能缓存的那一层,像这样
在这里插入图片描述

如何解决?

需要把嵌套的<router-view>拍平

也就是在路由守卫中添加一个将无用的 layout 布局过消除的方法:

router.beforeEach((to, from, next) => {
  ...
  handleKeepAlive(to)
  ...
}

/**
 * 递归处理多余的 layout : <router-view>,
 * 让需要访问的组件保持在第一层 index : <router-view> 之下
 * @param to
 */
function handleKeepAlive (to) {
  if (to.matched && to.matched.length > 2) {
    for (let i = 0; i < to.matched.length; i++) {
      const element = to.matched[i]
      if (element.components.default.name === 'layout') {
        to.matched.splice(i, 1)
        handleKeepAlive(to)
      }
    }
  }
}

/**
 * 方法二:兼容<layout>按需加载
 * @param to
 */
async function handleKeepAlive (to) {
  if (to.matched && to.matched.length > 2) {
    for (let i = 0; i < to.matched.length; i++) {
      const element = to.matched[i]
      if (element.components.default.name === 'layout') {
        to.matched.splice(i, 1)
        await handleKeepAlive(to)
      }
      // 如果没有按需加载完成则等待加载
      if (typeof element.components.default === 'function') {
        await element.components.default()
        await handleKeepAlive(to)
      }
    }
  }
}

没有进行 layout 移除时

在这里插入图片描述

layout 移除之后

在这里插入图片描述

接下来就可以愉快的缓存多层次的组件了

### Vue3 中 `keep-alive` 实现多级路由缓存 #### 使用场景与原理说明 为了提升用户体验,在单页应用(SPA)中,当用户切换不同视图时保持某些组件的状态不失效是非常重要的。Vue 提供了 `<keep-alive>` 组件来实现这一点。对于复杂的多级嵌套路由结构而言,可以通过组合使用 `<keep-alive>`, Vuex (或 Pinia), 和自定义逻辑来达到理想的缓存效果。 #### 配置方法 在项目中的 App.vue 文件内引入并配置如下代码: ```html <template> <div id="app"> <!-- 过渡动画 --> <transition name="fade-transform" mode="out-in"> <!-- KeepAlive 包裹 RouterView --> <keep-alive :include="cachedViews"> <router-view /> </keep-alive> </transition> </div> </template> <script setup lang="ts"> import { computed } from 'vue' import store from './store' // 假设已创建好 Store 实例 // 计算属性获取缓存列表 const cachedViews = computed(() => { return store.state.tagsView.cachedViews || [] }) </script> ``` 上述模板部分展示了如何利用计算属性动态绑定 `:include` 属性到存储于 Vuex 或者其他状态管理工具内的缓存路径数组上[^1]。 接着是在 router/index.ts 定义路由规则的时候加入 meta 字段用于标记哪些页面应该被缓存: ```typescript { path: '/example', component: () => import('@/views/Example'), children:[ { path:'child', component:() => import('@/views/example/Child'), meta:{ cache:true }, // 设置此字段表示子页面也需要缓存 } ], } ``` 最后一步就是在全局守卫里处理具体的缓存策略, 如下所示: ```javascript router.beforeEach((to, from, next)=>{ const hasCache = to.matched.some(record=>record.meta.cache); if(hasCache){ let viewName = getRouteKey(to); // 自定义函数返回唯一key作为缓存依据 store.dispatch('tagsView/addCachedView',viewName); } next(); }); ``` 这里通过遍历即将进入的目标路由匹配记录(`matched`)检查是否存在带有特定元数据 (`meta.cache=true`) 的项;如果存在,则调用相应的方法更新缓存队列[^2]。 #### 注意事项 需要注意的是,以上提到的方式适合那些页面是否需要缓存不会频繁改变的应用程序。如果有更复杂的需求比如按需加载、条件性刷新等情况则可能还需要进一步优化调整方案[^3]。
评论 20
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值