vue 全局前置守衛引起死循環的原因與解決方法

  • 2020 年 1 月 27 日
  • 筆記

我們經常會用到全局前置守衛,如判斷用戶有沒有登陸過,如果登陸過就直接跳到目的頁面,如果沒有登陸過,就跳轉到登陸頁。

先看官網對全局前置守衛的介紹

使用 router.beforeEach 註冊一個全局前置守衛:

const router = new VueRouter({ ... })    router.beforeEach((to, from, next) => {    // ...  })

當一個導航觸發時,全局前置守衛按照創建順序調用。守衛是非同步解析執行,此時導航在所有守衛 resolve 完之前一直處於 等待中

每個守衛方法接收三個參數:

  • to: Route: 即將要進入的目標 路由對象
  • from: Route: 當前導航正要離開的路由
  • next: Function: 一定要調用該方法來 resolve 這個鉤子。執行效果依賴 next 方法的調用參數。
    • next(): 進行管道中的下一個鉤子。如果全部鉤子執行完了,則導航的狀態就是 confirmed (確認的)。
    • next(false): 中斷當前的導航。如果瀏覽器的 URL 改變了 (可能是用戶手動或者瀏覽器後退按鈕),那麼 URL 地址會重置到 from 路由對應的地址。
    • next('/') 或者 next({ path: '/' }): 跳轉到一個不同的地址。當前的導航被中斷,然後進行一個新的導航。你可以向 next 傳遞任意位置對象,且允許設置諸如 replace: truename: 'home' 之類的選項以及任何用在 router-linkto proprouter.push 中的選項。
    • next(error): (2.4.0+) 如果傳入 next 的參數是一個 Error 實例,則導航會被終止且該錯誤會被傳遞給 router.onError() 註冊過的回調。

確保要調用 next 方法,否則鉤子就不會被 resolved

回到我們剛才所說驗證登陸使用全局前置守衛

router.beforeEach((to,from,next) =>{    if (sessionStorage.getItem("token")) {       if(to.path === "/login"){         next({path:"/dashboard"})       }       else{         alert("1")         next()       }    }else{      next({path: "/login"})   // 會再次執行前置導航守衛,因為路徑變化    }  })

上面的程式碼表面看沒有問題,

如果sessionStorage有token,並且如果即將要進入的目標路徑是登陸頁,就跳轉到/dashboard頁,如果是其它的頁面,就進入

如果sessionStorage沒有token 就進入登陸頁

但是程式碼執行會引起死循環,原因是沒有出口,執行next({path: "/login"})會再次執行全局前置導航守衛

程式碼改成下面的就正常了

router.beforeEach((to, from, next) => {    let token = window.sessionStorage.getItem('token');    if (to.path != '/login' && !token) {      next({        path: '/login'      })    } else {      if (to.path == '/login' && token) {        next('/dashboard')      } else {        next()      }    }  })

總結:執行next({ path: '/xxx' }) 跳到不同的地址都會再次執行 router.beforeEach 鉤子函數。