动态路由下的导航守卫--(to,from,next)

1. 导航守卫–(to,from,next)

router.beforeEach 注册一个全局前置守卫:

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

当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于 等待中(没有执行next就一直等待)。

每个守卫方法接收三个参数:

  • to: Route: 即将要进入的目标 路由对象
  • from: Route: 当前导航正要离开的路由
  • next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。
    • next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。
    • next(false): 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。
    • next(’/’) 或者 next({ path: ‘/’ }):跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向 next 传递任意位置对象,且允许设置诸如 replace: truename: 'home' 之类的选项以及任何用在 router-link 的 to proprouter.push 中的选项。
    • next(error): (2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。

确保要调用 next 方法,否则钩子就不会被 resolved

router.beforeEach((to, from, next) => {
    
    
  if (to.matched.length ===0) {
    
      //如果未匹配到路由
    from.path? next({
    
     path:from.path}) : next('/');   //如果上级也未匹配到路由则跳转主页面,如果上级能匹配到则转上级路由
  } else {
    
    
    next();    //如果匹配到正确跳转
  }
});

2. 匹配所有路由–(按照书写的路由顺序匹配)

路由的匹配规则是按照书写的顺序执行的,第一条匹配成功则不去匹配下一条,利用这一特性,可以在所有匹配路由的下面拦截匹配所有路由:

//创建路由对象并配置路由规则
let router = new VueRouter({
    
    
    routes:[
       {
    
    path:'/',redirect:{
    
    name:"home"}},  // 重定向到主页
       {
    
    name:'home',path:'/home',component:Home},
       {
    
    name:'login',path:'/login',component:Login},
       {
    
    path:'*',component:NotFound},//全不匹配的情况下,匹配NotFound组件,路由按顺序从上到下,依次匹配。最后一个*能匹配全部,
    ]
});

3,next的理解–(不带参是放行,带参是重新跳转)

其实在路由守卫中,只有next()是放行,其他的诸如:next('/logon') 、 next(to) 或者 next({ ...to, replace: true })都不是放行,而是:中断当前导航,执行新的导航

比如说现在我有一个守卫,在守卫中我使用next('/logon'),肯定有同学认为是会直接跳转到/logon路由:

beforeEach((to, from, next) => {
    
    
  next('/logon')
}

然而实际上,它是中断这次导航,重新跳转到新的路由,于是又会触发新的路由守卫,也就是下图这样:

beforeEach((to, from, next) => {
    
    
  beforeEach(('/logon', from, next) => {
    
    
  	 beforeEach(('/logon', from, next) => {
    
    
  	 	 beforeEach(('/logon', from, next) => {
    
    
  	 	 	beforeEac...  // 一直循环下去...... , 因为我们没有使用 next() 放行
 		}
 	 }
  }
}

如果把这个守卫改一下,当我在地址栏输入/home时:

beforeEach((to, from, next) => {
    
    
   if(to.path === '/home') {
    
    
   	next('/logon')
   } else {
    
    
    // 如果要去的地方不是 /home , 就放行
   	next()
   }
}

这样一来,我第一次进入导航守卫是“/home”,但是被重新跳转到“/logon”,于是再次进入导航守卫,这回就走的else路径,next()放行,浏览器中变成/logon。

4,动态添加路由addRoutes()

动态添加路由addRoutes()是异步的,这会导致一个问题,当我们在addRoutes()之后第一次访问被添加的路由会白屏,这是因为刚刚addRoutes()就立刻访问被添加的路由,然而此时addRoutes()没有执行结束,因而找不到刚刚被添加的路由导致白屏。因此需要从新访问一次路由才行。

例如我今天遇到的代码时这样的:

image-20211120112427346

这样的写法,看起来好像没啥问题,但是忽略了请求资源是异步的,addRoutes()也是异步的

先按照代码的设计者的逻辑过一遍(假设这两者都是同步的):

1,登录成功后,to.path是“/home”,这时候路由资源为空,发起请求获取到路由资源,使用addRoutes()添加路由,然后执行next()放行路由,于是浏览器变成/home,匹配上vue-router的路由,懒加载home/index.vue页面,然后发现有嵌套路由,需要重定向,于是变成“/home/homePage(假设是这个)”,再次进入导航守卫,因为已经有路由资源了,就直接next()放行,浏览器变成“/home/homePage",懒加载homePage的组件,于是渲染出页面。

然而,实际上呢,在第一次to.path是“/home”的时候,虽然发起获取路由资源的请求了,但它是异步的,还没执行完,更别提addRoutes()也是异步,更没执行完毕,于是就先执行底下的next()放行路由了!浏览器就变成/home了。

然而这时候,路由还没添加,肯定是匹配不到的对应路由的,于是白屏,需要等到路由资源请求回来了,并且添加到路由资源中了,才能匹配上路由(实际上这里又会进入一次守卫,可以简单理解为,浏览器在找不到路由时一直在轮询,直至查找到了再次进入导航守卫,执行next(),匹配路由,加载页面)

于是就可以知道,这个白屏的时间。其实取决于请求资源和添加路由的时间。

解决办法

此时就要使用next({ …to, replace: true })来确保addRoutes()时动态添加的路由已经被完全加载上去。

next({ …to, replace: true })中的replace: true只是一个设置信息,告诉VUE本次操作后,不能通过浏览器后退按钮,返回前一个路由。

因此next({ …to, replace: true })可以写成next({ …to }),不过你应该不希望用户在addRoutes()还没有完成的时候,可以点击浏览器回退按钮搞事情吧。

其实next({ …to })的执行很简单,它会判断:

如果参数to不能找到对应的路由的话,就再执行一次beforeEach((to, from, next)直到其中的next({ …to})能找到对应的路由为止。

也就是说此时addRoutes()已经完成啦,找到对应的路由之后,接下来将执行前往对应路由的beforeEach((to, from, next) ,因此需要用代码来判断这一次是否就是前往对应路由的beforeEach((to, from, next),如果是,就执行next()放行。

如果守卫中没有正确的放行出口的话,会一直next({ …to})进入死循环 !!!

因此你还需要确保在当addRoutes()已经完成时,所执行到的这一次beforeEach((to, from, next)中有一个正确的next()方向出口。

于是我把代码更改成如下的样子:

image-20211120121245756

因为登陆后第一次”/home“时,next({…to,replace:true})第一次执行时,资源必然没有加载好,于是再进入beforeRouter(to,from,next),这时候,如果还没准备好路由资源,则再进一层beforeRouter(to,from,next)直到路由资源准备好了,于是走else中的next()。

Guess you like

Origin blog.csdn.net/weixin_42349568/article/details/121438719
Recommended