网页的本质就是超级文本标记语言,通过结合使用其他的Web技术(如:脚本语言、公共网关接口、组件等),可以创造出功能强大的网页。因而,超级文本标记语言是万维网(Web)编程的基础,也就是说万维网是建立在超文本基础之上的。超级文本标记语言之所以称为超文本标记语言,是因为文本中包含了所谓“超级链接”点。
本篇文章给大家带来的内容是关于带你彻底搞定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
我们要想一下这些守卫都是怎么注册的,
好了我们要去榨取对应的守卫了,
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 关键词:带你完全搞定vue-Router的导航守卫