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