本文章存在缺陷,最后也没能够完善这个方案,大家看看就好。不过用这位大牛写的项目可以解决问题,配置也特别简单vue-navigaition。
最近在用vue尝试着做移动端的项目。希望实现前进刷新、后退不刷新的效果。即加载过的界面能缓存起来(返回不用重新加载),关闭的界面能被销毁掉(再进入时重新加载)。例如对a->b->c 前进(b,c)刷新,c->b->a 后退(b,a)不刷新。
由于keep-alive
会把所有加载过的界面都缓存起来,没法实现返回时将界面销毁掉,导致再进入时没有重新加载这个界面。于是首先想到的方案是在点击界面上返回按钮的时候,调用this.$destroy(true)
来将界面销毁掉。但是在移动端 android设备上会有物理返回键,如果通过物理返回键返回的话,就没法处理了。虽然可以重写android的返回事件,来调用js的方法,但是调用的是js的全局方法,没法具体让在最上层的那个界面销毁掉。
于是就需要另辟蹊径了。还好这篇文章给了我启发vue-router 之 keep-alive,多谢作者的分享。
要是能够知道路由是前进还是后退就好了,这样的话我就能在后退的时候让from
路由的keepAlive
置为false
,to
路由的keepAlive
置为ture
,就能在再次前进时,重新加载之前这个keepAlive
被置为false
的路由了。
废话不多说了,这里模拟有三个界面 login
到server
到main
。
首先我给这三个界面路由的path
设置了严格的层级关系 ,并设置keepAlive
都是true
,默认都是需要缓存。
const router = new Router({
routes: [
{
path: '/',
redirect: '/login'
},
{
path: '/login',
component: Login,
meta: {
keepAlive: true
}
},
{
path: '/login/server',
component: ServerList,
meta: {
keepAlive: true
}
},
{
path: '/login/server/main',
component: Main,
meta: {
keepAlive: true
}
}
]
})
由于这三个界面path的层级不同,我就能通过beforeEach
这个钩子判断出什么时候是后退了。在后退时将from
路由的keepAlive
置为false
,to
路由的keepAlive
置为ture
。
router.beforeEach((to, from, next) => {
const toDepth = to.path.split('/').length
const fromDepth = from.path.split('/').length
if (toDepth < fromDepth) {
console.log('后退。。。')
from.meta.keepAlive = false
to.meta.keepAlive = true
}
next()
})
最后需要缓存的界面用keep-alive
包起来,就能实现时前进刷新,后退时不刷新的效果了。
<keep-alive>
<router-view v-if="$route.meta.keepAlive">
<!-- 这里是会被缓存的视图组件 -->
</router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive">
<!-- 这里是不被缓存的视图组件 -->
</router-view>
补充说明
这个方法确实很取巧,像lyh2668
所说的单纯从length
判断会有很多场景有问题。比如 有一个page5
界面,我需要能从page3
和page4
界面都能跳转过去的话,那就需要为page5
配置两个路由path
了,假设page
几的path
层级就是几的话,那么就要为page5
配置层级4和层级5的两个路由路径了(当然直接就配置一个路由层级5的话也是可行的,只需要保证比跳转到它的界面的层级都大就可以)。
另外可以像lyh2668
所说的把keepAlive
换成noKeepAlive
,这样的话就不用在每个router
中去配置keepAlive
了,就方便了很多。关于层级也可以不通过/
来判断,比如在path
末尾加数字等其它方式来实现。
另外lyh2668
所说的压栈的方式,我也有搜到过,但是在有tab
界面的时候这种方式就不适合了,因为tab
界面切换的时候不需要这种效果,但也会被压栈。但是用我的方法的话,就可以把tab
界面的路由都设置为同一个层级,就不会有问题了
这是是压栈方式的代码,在没有tab界面的时候,这种方式确实会更好用。
let routerList = []
router.beforeEach((to, from, next) => {
if (routerList.length && routerList.indexOf(to.name) === routerList.length - 1) {
// 后退
routerList.splice(routerList.length - 1, 1)
to.meta.isBack = true
} else {
// 前进
routerList.push(from.name || '/')
to.meta.isBack = false
}
next()
})
欢迎有更好方法的朋友能够指点分享。
已添加Demo
说明,见github。包括普通界面切换、tab
界面切换和不同层级界面的切换示例。