理解 vue-router的beforeEach無限循環的問題


在理解beforeEach無限循環之前,我們先來看一下beforeEach相關的知識點,該篇文章的項目是基於 express+vue+mongodb+session實現注冊登錄 這篇文章項目基礎之上進行講解的,因為登錄完成后,會跳轉到列表頁面,那么在跳轉到列表頁面之前,我們會使用 router.js 使用beforeEach來判斷下,如果登錄成功,並且session在有效期內的話,就跳轉到下一個頁面去,否則的話,就重定向到登錄頁面去。
app/index/router.js 代碼如下:

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;

如上代碼寫完成后,我們從登錄頁面點擊登錄,登錄成功后,會跳轉至列表頁面。在跳轉之前會打印上面的信息。

console.log(to); 打印的信息如下:

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

如上代碼,調用 next()方法才能進入下一個路由,否則的如果寫了beforeEach方法,但是沒有調用next()方法的話,頁面會空白,不會跳轉到任何頁面的。默認不寫 router.beforeEach((to, from, next) => {}) 的話,它是默認跳轉到指定頁面去的,當然如果有這個方法的話,應該會覆蓋默認的方法。因此如果寫了調用了該方法的話,切記記得調用 next() 方法。

next(false); 如果next方法添加一個false的話,那么就會中斷當前的導航跳轉。修改完成后,我們重新刷新 localhost:8081/list 這個頁面的話,會看到頁面一片空白,最后我們打印下 console.log(to); 的信息如下:

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

next('/') 或者 next({ path: '/' }); 跳轉到一個不同的地址,當前的導航會被中段,然后會進入 path那個新的導航。

如上是基本的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即可。

因此我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的值,會執行下一個路由跳轉,因此能跳轉成功。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM