工作上需要把每一個路由都加上同一個 query string 第一直覺就是直接寫成這樣:

router.beforeEach(async (to, from, next) => {
  next({
    path: path, 
    query: {...to.queryl ,token: tokenStr}
  })
})

結果卻跳出 Maximum call stack size exceeded 的錯誤,判斷程式出現無窮迴圈:

runtime.js?96cf:285 Uncaught (in promise) RangeError: Maximum call stack size exceeded

第一個反應是傻眼貓咪,為什麼 next() 不傳入參數的時候不會出現無窮迴圈,但塞進參數就會, 難道說 next() 在傳入參數與不傳入參數的行為並不相同!! 因此去翻閱官網對 next() 的說明:

next: Function: this function must be called to resolve the hook. The action depends on the arguments provided to next:

next(): move on to the next hook in the pipeline. If no hooks are left, the navigation is confirmed.

… 中略

next(’/’) or next({ path: ‘/’ }): redirect to a different location. The current navigation will be aborted and a new one will be started. You can pass any location object to next, which allows you to specify options like replace: true, name: ‘home’ and any option used in router-link’s to prop or router.push

…下略

  • next() 不帶參數:會直接解開(resolve) Hook 使 navigation 往下繼續, 因此總共只會進入 Navigation Guard 1次;
  • next('/')next({...}),會放棄該次 navigation 進行重新導向(redirect), 而不是用參數繼續同一個 navigation,因此會再次進入 Navigation Guard。

=>重要:next('/')next({...}) 不會解開 beforeEach hook!! 重新導向後不帶參數的 next() 才繼續解開下一次的 beforeEach, 因此如果再次進入 Navigation Guard 後又呼叫同一個帶設定參數的 next({...}) 就會造成無窮迴圈了。

解決方法: 在給予參數的時候寫入判斷,如果參數沒有就加上參數重新 navigation; 如果有參數就呼叫 next() 解開 hook 完成該次 navigation, 因為加上參數重新導向後就可以呼叫 next(),所以不會出現無窮迴圈:

程式碼:

router.beforeEach(async (to, from, next) => {
  if (!to.query.key) {
    next({ path: to.path, query: { key: store.state.token } })
  } else {
    next()
  }
})

Reference

Vue-Router navigation-guards