vue实现前进刷新,后退不刷新

42,871 阅读2分钟

本文章存在缺陷,最后也没能够完善这个方案,大家看看就好。不过用这位大牛写的项目可以解决问题,配置也特别简单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置为falseto路由的keepAlive置为ture,就能在再次前进时,重新加载之前这个keepAlive被置为false的路由了。

废话不多说了,这里模拟有三个界面 loginservermain

首先我给这三个界面路由的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置为falseto路由的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界面,我需要能从page3page4界面都能跳转过去的话,那就需要为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界面切换和不同层级界面的切换示例。