Vue 使用 <keep-alive include> 实现嵌套 <router-view> 缓存,无限层次缓存
使用 <keep-alive> 实现缓存目前主流的有两种方法
方法一:
- 在路由元信息中添加缓存标识:
{
path: 'json',
name: 'json',
meta: {
...
keepAlive: true
...
},
component: () => import('../views/components/json')
},
- 使用 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>
组件等。
然鹅,我有强迫症,不能接受没有动效 ( ̄(∞) ̄)
因此这里重点讲方法二:
1.使用vuex将路由元信息中keepAlive: true
的路由记录下来,将路由的name属性为维护在一个keepAliveList: []
里。
2.使用<keep-alive>
的include
属性,来实现动态的组件缓存。
先说一下include
属性,它的值可以是:字符串,正则表达式,数组
首先我们需要知道keep-alive
可以根据include
中的值来匹配当前路由的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 移除时
layout 移除之后
接下来就可以愉快的缓存多层次的组件了
上一篇: 页面嵌套的实现