理解 vue-router的beforeEach无限循环的问题

在理解beforeEach无限循环以前,咱们先来看一下beforeEach相关的知识点,该篇文章的项目是基于 express+vue+mongodb+session实现注册登陆 这篇文章项目基础之上进行讲解的,由于登陆完成后,会跳转到列表页面,那么在跳转到列表页面以前,咱们会使用 router.js 使用beforeEach来判断下,若是登陆成功,而且session在有效期内的话,就跳转到下一个页面去,不然的话,就重定向到登陆页面去。
app/index/router.js 代码以下:html

import Vue from 'vue';
import VueRouter from 'vue-router';

// 告诉 vue 使用 vueRouter
Vue.use(VueRouter);

const routes = [
  {
    path: '/list',
    name: 'list',
    component: resolve => require(['./views/list'], resolve)
  },
  {
    path: '*', // 其余没有的页面都重定向到 home页面去
    redirect: '/login'
  },
  {
    path: '/login',
    name: 'login',
    component: resolve => require(['./views/login'], resolve)
  },
  {
    path: '/regist',
    name: 'regist',
    component: resolve => require(['./views/regist'], resolve)
  }
]

var router = new VueRouter({
  base: '/app/index', // 配置单页应用的基路径
  routes: routes
});

router.beforeEach((to, from, next) => {
  console.log(to);   // 即将要进入路由的对象
  console.log(from); // 当前导航要离开的路由对象
  console.log(next); // 调用next该方法,才能进入下一个路由
  next();
});
export default router;

如上代码写完成后,咱们从登陆页面点击登陆,登陆成功后,会跳转至列表页面。在跳转以前会打印上面的信息。vue

console.log(to); 打印的信息以下:vue-router

console.log(from); 打印的信息以下:mongodb

如上代码,调用 next()方法才能进入下一个路由,不然的若是写了beforeEach方法,可是没有调用next()方法的话,页面会空白,不会跳转到任何页面的。默认不写 router.beforeEach((to, from, next) => {}) 的话,它是默认跳转到指定页面去的,固然若是有这个方法的话,应该会覆盖默认的方法。所以若是写了调用了该方法的话,切记记得调用 next() 方法。express

next(false); 若是next方法添加一个false的话,那么就会中断当前的导航跳转。修改完成后,咱们从新刷新 localhost:8081/list 这个页面的话,会看到页面一片空白,最后咱们打印下 console.log(to); 的信息以下:session

打印 console.log(from); 打印信息以下所示:app

next('/') 或者 next({ path: '/' }); 跳转到一个不一样的地址,当前的导航会被中段,而后会进入 path那个新的导航。post

如上是基本的beforeEach的语法,那么在具体跳转到那个页面去,我想在beforeEach中判断下,若是用户登陆成功的话,而且session在有效期的话,那么就跳转到指定的页面去,不然的话,就重定向到登陆页面去。
先看登陆成功后的代码以下:测试

this.$http.post('/reglogin/login', obj).then((res) => {
  if (res.body.code === 0) {
    // 登陆成功
    const msg = res.body.msg || '登陆成功!!!';
    localStorage.setItem('username', 1);
    this.messageFunc('success', msg);
    setTimeout(() => {
      this.$router.push({
        path: '/list'
      });
    }, 2000);
  } else {
    // 登陆失败
    const errorMsg = res.body.errorMsg || '登陆失败';
    this.messageFunc('warning', errorMsg);
  }
});

登陆成功后,咱们会把 username 保存到 localStorage 中,咱们就能够在 beforeEach中来判断 localStorage 中是否有username了,固然若是登陆过时的话或者注销的话,须要把本地存储的 username该字段置为null便可。ui

所以我router.js代码会改为以下所示:

router.beforeEach((to, from, next) => {
  console.log(localStorage.getItem('username'));
  if (localStorage.getItem('username') === null) {
    console.log('出现死循环了');
    // 重定向到登陆页面去
    next({
      path: '/login'
    });
  } else {
    console.log('我是来测试的');
  }
});

如上代码后会, 当咱们刷新下 http://localhost:8081/#/login 后会出现死循环,以下所示:

如上而且页面是空白的,由于当咱们刷新 http://localhost:8081/#/login 后先会执行 router.beforeEach()该方法,由于咱们尚未登陆,因此获取到本地存储的username为null,所以一直重定向到 /login 登陆页面去,所以会一直出现死循环,按道理来讲重定向到登陆页面去的时候,页面是登陆页面,不该该是空白,可是有可能出现死循环了,致使一直重定向,最后什么缘由致使空白了。

可是若是咱们改为以下代码就一切正常了,以下代码:

router.beforeEach((to, from, next) => {
  /*
  console.log(localStorage.getItem('username'));
  if (localStorage.getItem('username') === null) {
    console.log('出现死循环了');
    // 重定向到登陆页面去
    next({
      path: '/login'
    });
  } else {
    console.log('我是来测试的');
  }
  */
  if (true) {
    // 若是为true的话,会重定向到指定页面去,不然的话会重定向到登陆页面去
    next();
  } else {
    next({
      path: '/login'
    });
  }
});

如上代码,我直接写死了为true,则直接调用 next()方法跳转到下一个路由,就不会再调用 beforeEach()方法了。若是为false的话,则跳转到 登陆('/login') 页面去。

所以为了解决 全局 判断用户是否登陆跳转的问题,咱们可使用以下方法解决:

router.beforeEach((to, from, next) => {
  if (localStorage.getItem('username')) { // 若是已经登陆的话
    next();
  } else {
    if (to.path === '/login') { // 若是是登陆页面的话,直接next()
      next();
    } else { // 不然 跳转到登陆页面
      next({
        path: '/login'
      });
    }
  }
});

固然在登陆接口中,登陆成功后,要使用 localStorage 保存用户名,以下 app/index/views/login.vue 接口以下代码:

this.$http.post('/reglogin/login', obj).then((res) => {
  if (res.body.code === 0) {
    // 登陆成功
    const msg = res.body.msg || '登陆成功!!!';

    // 这里须要使用 localStorage 保存用户名 
    localStorage.setItem('username', 1);

    this.messageFunc('success', msg);
    setTimeout(() => {
      this.$router.push({
        path: '/list'
      });
    }, 2000);
  } else {
    // 登陆失败
    const errorMsg = res.body.errorMsg || '登陆失败';
    this.messageFunc('warning', errorMsg);
  }
});

固然 点击注销的时候 也要删除该 username了,以下代码:

// 注销
logout() {
  this.$http.post('/reglogin/logout', {}).then((res) => {
    if (res.body.code === 1 && res.body.session) {
      // 说明注销成功 跳转到登陆页面去
      this.$message({
        message: '注销成功',
        type: 'success'
      });
      localStorage.removeItem('username');
      setTimeout(() => {
        this.$router.push({
          path: '/login'
        });
      }, 1000);
    }
  });
}

咱们首先看 beforeEach 代码,

router.beforeEach((to, from, next) => {
  if (localStorage.getItem('username')) { // 若是已经登陆的话
    next();
  } else {
    if (to.path === '/login') { // 若是是登陆页面的话,直接next()
      next();
    } else { // 不然 跳转到登陆页面
      next({
        path: '/login'
      });
    }
  }
});

to 参数是到哪里去的含义,所以当咱们刷新登陆页面的时候,from 的值路径为 '/', to 的path为 '/login', 所以调用next方法,仍是登陆页面,不会出现死循环,和咱们上面讲的以下代码原理是同样的:

router.beforeEach((to, from, next) => {
  if (true) {
    // 若是为true的话,会重定向到指定页面去,不然的话会重定向到登陆页面去
    next();
  } else {
    next({
      path: '/login'
    });
  }
});

而后当我登陆成功后,/login 接口会使用 localStorage 保存username信息,而后在跳转页面以前会继续调用 beforeEach方法,而后会判断:

if (localStorage.getItem('username')) { // 若是已经登陆的话
  next();
} 

说明有username的值,会执行下一个路由跳转,所以能跳转成功。

相关文章
相关标签/搜索