您好,欢迎来到三六零分类信息网!老站,搜索引擎当天收录,欢迎发信息

带你彻底搞定vue-Router的导航守卫

2025/5/16 11:00:23发布22次查看
本篇文章给大家带来的内容是关于带你彻底搞定vue-router的导航守卫,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。
vue-router导航守卫在本期文章中,我将为大家梳理弄明白以下几个事情,
1:导航守卫的执行顺序是怎么样的?2:导航守卫中的next的用处?3:为什么aftereach守卫没有next?4:beforeeach是否可以叠加?5:路由跳转经历了哪几部分?在之前说过的一个内容router实例的history属性帮助我们做了所有跳转部分的事情,所以导航守卫的内容也在history中。
我们以html5history这个类来看一下这个push方法,
push (location: rawlocation, oncomplete?: function, onabort?: function) {    const { current: fromroute } = this    this.transitionto(location, route => {      pushstate(cleanpath(this.base + route.fullpath))      handlescroll(this.router, route, fromroute, false)      oncomplete && oncomplete(route)    }, onabort)  }
push(我们跳转时的$router.push就是这个方法)过程中调用了transitionto完成了一系列的跳转内容,但这个方法在html5的类中并不存在,继承于base.js类中的方法
transitionto就是实现路由跳转的方法
transitionto的主流程是由confirmtranstion方法于uodateroute方法结合起来的,翻译成普通话:路由跳转要先经过一个确认跳转的过程,在确认过程完成后进行一次路由的更新操作,
transitionto (location: rawlocation, oncomplete?: function, onabort?: function) {    // 获取要跳转的并且经过处理的路由    const route = this.router.match(location, this.current)    // confirmtranstion确认跳转过程    this.confirmtransition(route, () => {      // 确认完毕后完成更新路由操作      this.updateroute(route)      oncomplete && oncomplete(route)      this.ensureurl()      // fire ready cbs once      if (!this.ready) {        this.ready = true        this.readycbs.foreach(cb => { cb(route) })      }    }, err => {      if (onabort) {        onabort(err)      }      if (err && !this.ready) {        this.ready = true        this.readyerrorcbs.foreach(cb => { cb(err) })      }    })  }
confirmtransiton做了什么呢?首先判断一下你是不是相同的路由。如果是那就什么都不做,第二步呢,我们要开始收集一波守卫了,然后把守卫收集起来,然后把每个守卫执行一遍,confirmtransition就算执行成功了。
下面是部分源码截图:
这个过程中的难点是什么?
如何收集守卫组合成守卫队列?如何按顺序执行守卫的同时可以随时中止守卫队列?如何找到守卫队列执行完毕后的那个节点(守卫队列执行完可以通知一下)在vue-router中封装了一个runqueue函数来解决上面的三个问题的后两个。第一个问题呢则涉及到vue-router处理路由的一个大篇章,我们着重讲一下runqueue函数
runqueue函数的思想:1:迭代器模式来保证遍历队列时每一步都是可控的,
2:队列完成后执行对应的回调函数,
推断出函数参数的对应功能:
queue : 需要执行的守卫队列
fn    : 迭代器函数,守卫队列的每一个守卫都去执行迭代器函数
fn的第二个参数使迭代器进入下一步,不掉用就不会进入下一步(很重点)cb    : 结束时调用的回调函数
export function runqueue (queue: array<?navigationguard>, fn: function, cb: function) {  const step = index => {  // 队列里已经没有内容可以执行了,那就代表队列执行完成了    if (index >= queue.length) {      cb()    } else {      // 如果队列内容存在就执行迭代函数      if (queue[index]) {        fn(queue[index], () => {          step(index + 1)        })      // 什么也没有那就到下一步了              } else {        step(index + 1)      }    }  }  // 启动了  step(0)}
runqueue是怎么帮助我们解决守卫队列处理的问题就算说完了。
(快留起来,这函数简直吊极了!)处理守卫队列的大锤子我们已经制造好了,可以开工了,那你的守卫队列呢??
对对对,还有守卫队列要收集。
这个时候我们要想想有哪些守卫?
守卫有两大种类:前置守卫、后置守卫。前置守卫:
全局的前置守卫: beforeeach beforeresolve
路由独享的守卫: beforeenter
组件内的守卫:    beforerouterenter、beforerouterupdate、beforerouteleave
后置守卫:
全局的后置守卫: aftereach
我们要想一下这些守卫都是怎么注册的,
在路由实例注册的:
beforeeach、beforeresolve、aftereach
在路由配置中注册的(路由独享守卫):
beforeenter
组件内的路由守卫:
beforerouteleave、beforerouteupdate、beforerouteenter
好了我们要去榨取对应的守卫了,
confirmtransition的守卫分为两个队列:我们先来看第一个队列
 // 拿到路由跳转中更新、摧毁、激活时对应展示的组件。 const {      updated,      deactivated,      activated    } = resolvequeue(this.current.matched, route.matched)    // 路由守卫    const queue: array<?navigationguard> = [].concat(      // in-component leave guards      extractleaveguards(deactivated),      // global before hooks      this.router.beforehooks,      // in-component update hooks      extractupdatehooks(updated),      // in-config enter guards      activated.map(m => m.beforeenter),      // async components      resolveasynccomponents(activated)    )
一个queue的顺序:
拿到被摧毁的组件的,榨取出所有组件内的离开守卫。
全局的beforeeach组件。
拿到更新的所有组件,榨取出所有组件内的更新守卫。
遍历要进入的路由,拿到所有路由的独享守卫。
加载要被激活的异步组件
7个守卫中的4个守卫都在被按顺序拿出来了,放入第一个queue。
再下一步要有一个处理守卫的迭代器:
我们该如何处理守卫?保证在守卫中可以停止并且跳转到其余路由,
保证守卫可以正常通过,
const iterator = (hook: navigationguard, next) => {      if (this.pending !== route) {        return abort()      }      try {        hook(route, current, (to: any) => {          // 传个false就直接执行路由的错误处理,然后停止什么都不做。          if (to === false || iserror(to)) {            // next(false) -> abort navigation, ensure current url            this.ensureurl(true)            abort(to)          } else if (          // 如果我们接受了一个可以操作的路径。            typeof to === 'string' ||            (typeof to === 'object' && (              typeof to.path === 'string' ||              typeof to.name === 'string'            ))          ) {            // next('/') or next({ path: '/' }) -> redirect            abort()            // 我们就执行路由跳转操作,并且守卫队列停止下面的迭代            if (typeof to === 'object' && to.replace) {              this.replace(to)            } else {              this.push(to)            }          } else {            // confirm transition and pass on the value            // 接续迭代下去咯            next(to)          }        })      } catch (e) {        abort(e)      }    }
next函数,之前在将runqueue的函数的时候,fn接收第二个参数(之前画过重点),第二个参数的回调函数是完成迭代器向下一步执行的功能。
下面会有一点乱:
所有的前置守卫都接收三个参数
beforeenter(to,from,next)=>{    //这个next就是我们看到的 hook里面接收的箭头函数((to:any)=>{})    //这个箭头函数里面对迭代器的next进行了一下掉用,    //保证在一定情况下迭代器可以向下走一步。    next('/index')    // 我们在这种next('/index')传递一个可以执行的路径时,(to:any)=>{}    //这个箭头函数并不会调用迭代的next,而是跳转别的路径执行了push操作。    // 如果我们不掉用守卫中的next,迭代器的next肯定并不会执行,守卫的迭代就停止了,    // 守卫堵塞confirmtransition并不会执行完毕,也就不会由后面的更细路由操作了。}
runqueue(queue, iterator, () => {      const postentercbs = []      const isvalid = () => this.current === route      // wait until async components are resolved before      // extracting in-component enter guards      const enterguards = extractenterguards(activated, postentercbs, isvalid)      const queue = enterguards.concat(this.router.resolvehooks)      runqueue(queue, iterator, () => {        if (this.pending !== route) {          return abort()        }        this.pending = null        oncomplete(route)        if (this.router.app) {          this.router.app.$nexttick(() => {            postentercbs.foreach(cb => { cb() })          })        }      })    })
我们在把第一个queue(四个守卫与一个异步组件的加载)执行完毕后,要收集与执行第二个queue了,
第二个queue:
收集了被的激活组件内的进入守卫
全局的beforeresolve的守卫
收集完开始执行第二个queue的迭代。第二个queue执行完执行一下oncomplete函数,代表着confirmtransition方法执行完毕了。确认路由的过程结束了,
下面就是updateroute的过程。updateroute的时候执行全部的后置守卫,因为更新路由之后,当前的路由已经变化了,所以在给守卫传参数的时候缓存了一下,之前的路由。
updateroute (route: route) {    const prev = this.current    this.current = route    this.cb && this.cb(route)    this.router.afterhooks.foreach(hook => {      hook && hook(route, prev)    })  }
所以为什么aftereach没有next呢?因为aftereach根本不在迭代器之内,他就没有next来触发迭代器的下一步。
最后我们说一下beforeeach的内容:
我们设置beforeeach全局守卫的时候,守卫们存储在哪里?
beforeeach (fn: function): function {    return registerhook(this.beforehooks, fn)}function registerhook (list: array<any>, fn: function): function {  list.push(fn)  // 返回值是一个function  return () => {    const i = list.indexof(fn)    if (i > -1) list.splice(i, 1)  }}
这段代码beforeeach是通过注册守卫的方式,将注册的全局前置守卫放在beforehooks的容器内,这个容器里面装载着所有的前置守卫
一家人(全局的 前置进入、前置resolve、后置守卫)整整齐齐的放在对应的容器里面,容器是个数组,所以注册全局守卫的时候,是支持注册多个的,
router.beforeeach(()=>{xxx});router.beforeeach(()=>{yyy});// 这两个守卫都会执行,只是先注册的先执行,// registerhook这个方法还可以清除对应的守卫,这个方法也可以使用
总结我们来回答一下开篇的5个问题
1:导航守卫的执行顺序是怎么样的?beforerouteleave < beforeeach < beforerouteupdate < beforeenter < beforerouteenter < beforeresolve < aftereach
2:导航守卫中的next的用处?next的作用,使导航守卫队列的继续向下迭代
3:为什么aftereach守卫没有next?aftereach根本不在导航守卫队列内,没有迭代的next
4:beforeeach是否可以叠加?beforeeach是可以叠加的,所有的全局前置守卫按顺序存放在beforehooks的数组里面,
5:路由跳转经历了哪几部分?路由跳转的核心方法是transitionto,在跳转过程中经历了一次confirmtransition,
(beforerouteleave < beforeeach < beforerouteupdate < beforeenter < 异步组件加载)这样顺序的queue为第一个,
在第一个queue迭代完毕后,执行第二个(beforerouteenter < beforeresolve)这样顺序的queue,
在执行完毕后,开始执行updateroute,之后执行全局的aftereach守卫。最后完成路由的跳转。
5个问题解答完毕,希望对你的业务有帮助。
以上就是带你彻底搞定vue-router的导航守卫的详细内容。
该用户其它信息

VIP推荐

免费发布信息,免费发布B2B信息网站平台 - 三六零分类信息网 沪ICP备09012988号-2
企业名录 Product