目录
  • 前言
  • Demo项目结构
  • 路由层级扁平化
  • 给所有的 router-view 都嵌套上 keep-alive

前言

keep-alive是Vue中的缓存标签, 组件在标签中的内容会被缓存下来;但是在多层嵌套的router-view中, 只能缓存到该层下的router-view, 由于路由嵌套比较常见,所以这里提供两种我觉得OK的解决方案。

解决思路

  • 路由层级扁平化,在路由守卫中执行一个拍平的函数,将需要缓存的路由提升到第一层,这样处理会影响到路由层级,最直白的影响如对 面包屑 等功能有直接影响
  • 把所有的 router-view 都通过 keep-alive 包裹起来, 通过keep-aliveinclude, exclude 来判断是否需要缓存。

Demo项目结构

router.ts

路由的结构大概是这样的 Layout > TableManage > (List, Detail, Add…)

路由层级扁平化

在路由的 afterEach 执行一个扁平化方法, 举例:

 1
 2
 31.  import router from  '@/router/index'
 42.  import  PageTitleUtils  from  '@/utils/PageTitleUtils'
 53.  import  {  ElMessage  }  from  'element-plus'
 64.  import useStore from  '@/store/index'
 75.  import type {  RouteLocationNormalized,  NavigationGuardNext  }  from  'vue-router'
 86.  import  NProgress  from  'nprogress'
 97.  import  'nprogress/nprogress.css'
10
119.  NProgress.configure({ showSpinner:  false  })  // NProgress Configuration
12
1311.  const  { user, routeStore }  = useStore()
14
1513.  const whiteList =  \['/login'\]  // no redirect whitelist
16
1715.  router.beforeEach(async (to:  RouteLocationNormalized,  from:  RouteLocationNormalized,  next:  NavigationGuardNext)  =>  {
1816.    NProgress.start()
1917.    if  (user.hasToken())  {
2018.    if  (to.path ===  '/login')  {
2119.    next({ path:  '/'  })
2220.    NProgress.done()
2321.    }  else  {
2422.    if  (user.hasUserInfo())  {
2523.    next()
2624.    NProgress.done()
2725.    }  else  {
2826.    try  {
2927.   await user.getUserInfo()
3028.   await routeStore.setRoutes()
3129.    next({  ...to, replace:  true  })
3230.    NProgress.done()
3331.    }  catch  (error: any)  {
3432.   routeStore.resetRoutes()
3533.   user.resetUserInfo()
3634.    ElMessage.error(error ||  'Has Error')
3735.    next(`/login?redirect=${to.fullPath}`)
3836.    NProgress.done()
3937.    }
4038.    }
4139.    }
4240.    }  else  {
4341.    /\* has no token */
4442.    if  (whiteList.indexOf(to.path)  !==  -1)  {
4543.    // in the free login whitelist, go directly
4644.    next()
4745.    NProgress.done()
4846.    }  else  {
4947.    // other pages that do not have permission to access are redirected to the login page.
5048.    next(`/login?redirect=${to.fullPath}`)
5149.    NProgress.done()
5250.    }
5351.    }
5452.  })
55
5654.  router.afterEach((to: any)  =>  {
5755.    NProgress.done()
5856.    // set page title
5957.    const  { meta }: any = to
6058.   document.title =  PageTitleUtils.getPageTitle(meta.title)
6159.    // delayering router
6260.   delayeringRoute(to)
6361.  })
64
6563.  /**
6664.   \* 递归处理多余的 layout : <router-view>,
6765.   \* 让需要访问的组件保持在第一层 index : <router-view> 之下
6866.   */
6967.  function delayeringRoute(to:  RouteLocationNormalized)  {
7068.    if  (to.matched && to.matched.length >  2)  {
7169.    for  (let i =  0; i < to.matched.length; i++)  {
7270.    const element = to.matched\[i\]
7371.    // 移除多余的 layout, 也许你项目中并不叫 layout ,自行修改此处
7472.    if  (element.components?.default.name ===  'layout')  {
7573.   to.matched.splice(i,  1)
7674.   handleKeepAlive(to)
7775.    }
7876.    }
7977.    }
8078.  }
81

以上代码示例中, delayeringRoute 是移除多余 layout 的方法, 在路由的afterEach方法中去移除,弊端很明显, 路由的结构受到了影响

给所有的 router-view 都嵌套上 keep-alive

这是我比较推荐的一种方法, 没什么副作用,也不麻烦, 毕竟所有的 router-view 都一样,如果你使用的IDE是vscode,可以直接把这段代码写进项目中的代码片段, 方便使用。

Layout的Main

 1
 2
 31.  <script  lang="ts"  setup  name="AppMain">
 42.    import  { useRoute } from 'vue-router'
 53.    import useStore from '@/store';
 64.    import  { storeToRefs } from 'pinia';
 7
 86.    const route = useRoute()
 97.    const  { tagview }  = useStore()
108.    const  { cacheList }  = storeToRefs(tagview)
119.  </script>
12
1311.  <template>
1412.    <div  class="app-main">
1513.    <router-view  v-slot="{ Component }">
1614.    <keep-alive :include="cacheList">
1715.    <component :is="Component" :key="route.fullPath"  />
1816.    </keep-alive>
1917.    </router-view>
2018.    </div>
2119.  </template>
22
2321.  <style  scoped  lang="scss">
2422.  </style>
25

其他嵌套的路由, 不管几层,都可以这样处理

 1
 2
 31.  <script  lang="ts"  setup  name="TableManage">
 42.    import  { useRoute } from 'vue-router'
 53.    import useStore from '@/store';
 64.    import  { storeToRefs } from 'pinia';
 75.    import  { onMounted } from 'vue';
 8
 97.    const route = useRoute()
108.    const  { tagview }  = useStore()
119.    const  { cacheList }  = storeToRefs(tagview)
12
1311.   onMounted(()  =>  {
1412.   tagview.addCacheList('TableManage')
1513.    })
16
1715.  </script>
18
1917.  <template>
2018.    <router-view  v-slot="{ Component }">
2119.    <keep-alive :include="cacheList">
2220.    <component :is="Component" :key="route.fullPath"  />
2321.    </keep-alive>
2422.    </router-view>
2523.  </template>
26

这样就可以实现嵌套路由的缓存了。 附上演示

到此这篇关于Vue3嵌套路由中使用keep-alive缓存多层的实现的文章就介绍到这了,更多相关Vue3 keep-alive缓存多层内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

个人笔记记录 2021 ~ 2025