vue-router router.beforeEach浅析

准备知识

vue-router几个钩子函数,vue-router钩子函数官方解释

遇到问题

在用vue-element-admin做一个权限记录时,遇到了一个问题,问题描述:

   动态增加的用户权限时,在动态增加权限页面按下F5刷新的时候,会必现无法加载到正确的组件,直接跳转到/404页面

动态增加路由的代码如下:

```javascript

router.beforeEach(async (to, from, next) => {

// start progress bar

  NProgress.start()

// set page title

  document.title =getPageTitle(to.meta.title)

// determine whether the user has logged in

  const hasToken =getToken()

if (hasToken) {

if (to.path ==='/login') {

// if is logged in, redirect to the home page

      next({path:'/'})

NProgress.done()

}else {

const hasGetUserInfo = store.getters.name

      if (hasGetUserInfo) {

next()

}else {

try {

// get user info

          console.log(`to ----> `, to)

await store.dispatch('user/getInfo')

if (store.getters.roles.length !==0) {// 判断当前用户是否已拉取完user_info信息

            const roles = store.getters.roles

            const accessRoutes =await store.dispatch('GenerateRoutes', {roles})

router.addRoutes(accessRoutes)

next({...to, replace:true})

          }

        }catch (error) {

// remove token and go to login page to re-login

          await store.dispatch('user/resetToken')

Message.error(error ||'Has Error')

next(`/login?redirect=${to.path}`)

NProgress.done()

}

}

}

}else {

/* has no token*/

    if (whiteList.indexOf(to.path) !== -1) {

// in the free login whitelist, go directly

      next()

}else {

// other pages that do not have permission to access are redirected to the login page.

      next(`/login?redirect=${to.path}`)

NProgress.done()

}

}

})

```   

路由定义:

```javascript

export const constantRouterMap = [

{

path:'/login',

    component: () =>import('@/views/login/index'),

    hidden:true

  },

  {

path:'/register',

    component: () =>import('@/views/login/register'),

    hidden:true

  },

  {

path:'/edit_password',

    component: () =>import('@/views/login/editPassword'),

    hidden:true

  },

  {

path:'/404',

    component: () =>import('@/views/404'),

    hidden:true

  },

  {

path:'/',

    component: Layout,

    redirect:'/dashboard',

    children: [{

path:'dashboard',

      name:'首页',

      component: () =>import('@/views/dashboard/index'),

      meta: {title:'首页', icon:'home'}

}]

}

]

},

]

},

  // 404 page must be placed at the end !!!

  {path:'*', redirect:'/404', hidden:true}

]

export const asyncRouterMap = [

  {

path:'/configuration_page',

    component: Layout,

    children: [

{

path:'/conf_page',

        name:'ConfPage',

        component: ConfPage,

        meta: {title:'配置页面', icon:'conf_page', role: ['sysadmin']}

}

]

}

]

const createRouter = () =>new Router({

// mode: 'history', // require service support

  scrollBehavior: () => ({y:0}),

  routes: constantRouterMap

})

const router =createRouter()

// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465

export function resetRouter () {

const newRouter =createRouter()

router.matcher = newRouter.matcher // reset router

}

export default router

```   

定位问题

1.刷新时在router.beforeEach加了打印回调to的值,发现to此时已经变为/404,所以问题应该不是出现在动态增加路由的地方

2.查看了router官方文档,

完整的导航解析流程导航被触发。

1.在失活的组件里调用离开守卫。

2.调用全局的 beforeEach 守卫。

3.在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。

4.在路由配置里调用 beforeEnter。

5.解析异步路由组件。

6.在被激活的组件里调用 beforeRouteEnter。调用全局的 beforeResolve 守卫 (2.5+)。导航被确认。调用全局的 afterEach 钩子。触发 DOM 更新。

7.用创建好的实例调用 beforeRouteEnter 守卫中传给 next 的回调函数

官网并没有指明vue是什么时候去匹配路由的,继续网上查找资料,终于在查到了这句话

理论上应该是这样的,但问题是next 方法不是异步的,每当触发router 的勾子时,实际上目标的 route 已经获取完成了,不会再去重新获取,导致即使进行了 commit,也不会去取最新的。可以参考下源码:https://github.com/vuejs/vue-router/blob/d539788df8394efe41c1534e1e9d1555aa2edbe2/src/history/base.js#L163 248这个 route 在执行 next 的时候是不会产生变化的,在第一次导航时候应该就已经确定了。

通过这段话基本确认了在进入beforeEach之前,vue就已经根据静态配置的路由匹配到了/404,这个段话还可以对比vue的源码继续深入研究一下,刷新的具体过程,后续继续学习。

综上的分析:为了让正确的匹配,把/404这个path的匹配到的path放到动态的路由里面,使path不能在静态路由里面匹配,直到动态路由匹配,问题解决。修改代码如下:

```javascript

export const constantRouterMap = [

{

path:'/login',

    component: () =>import('@/views/login/index'),

    hidden:true

  },

  {

path:'/register',

    component: () =>import('@/views/login/register'),

    hidden:true

  },

  {

path:'/edit_password',

    component: () =>import('@/views/login/editPassword'),

    hidden:true

  },

  {

path:'/404',

    component: () =>import('@/views/404'),

    hidden:true

  },

  {

path:'/',

    component: Layout,

    redirect:'/dashboard',

    children: [{

path:'dashboard',

      name:'首页',

      component: () =>import('@/views/dashboard/index'),

      meta: {title:'首页', icon:'home'}

}]

}

]

},

]

}

]

export const asyncRouterMap = [

  {

path:'/configuration_page',

    component: Layout,

    children: [

{

path:'/conf_page',

        name:'ConfPage',

        component: ConfPage,

        meta: {title:'配置页面', icon:'conf_page', role: ['sysadmin']}

}

]

},

// 404 page must be placed at the end !!!

  {path:'*', redirect:'/404', hidden:true}

]

const createRouter = () =>new Router({

// mode: 'history', // require service support

  scrollBehavior: () => ({y:0}),

  routes: constantRouterMap

})

const router =createRouter()

// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465

export function resetRouter () {

const newRouter =createRouter()

router.matcher = newRouter.matcher // reset router

}

export default router

```  

特别说明

router.addRoutes(accessRoutes)并不会改变router.options.routes的值,router.options.routes在初始化就已经确定了的,

那addRoutes的实现原理待深入分析

你可能感兴趣的:(vue-router router.beforeEach浅析)