vue-router中的导航守卫就是核心就是把所有方法组成也给数组,依次调用
这里我们拿beforeEach举例。
先来回顾下beforeEach的简单使用,我们可以使用 router.beforeEach 注册一个全局前置守卫,当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于 等待中。
每个守卫方法接收三个参数:
- to: Route: 即将要进入的目标 路由对象
- from: Route: 当前导航正要离开的路由
- next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。
const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
// ...
})
router.beforeEach((to, from, next) => {
setTimeout(() => {
console.log('hello')
next()
}, 1000)
})
router.beforeEach((to, from, next) => {
setTimeout(() => {
console.log('world')
next()
}, 1000)
})
export default router
上述代码运行后会隔一秒控制台输出hello,再隔一秒控制台输出world。
从上面的示例可以看出,beforeEach是按照创建顺序调用的,我们可以将beforeEach参数中的回调放入一个数组中,然后依次执行数组中的函数。
vue-router/index.js
export default class VueRouter {
constructor(options) {
// ...
// 用于存放beforeEach传入的回调函数
this.beforeEachHooks = []
// ...
}
// ...
// beforeEach函数
beforeEach(fn) {
// 将beforeEach传入的函数放入数组
this.beforeEachHooks.push(fn)
}
// ...
}
VueRouter.install = install
vue-router/history/base.js
// ...
function runQueue(queue, iterator, cb) {
function next(index) {
if (index >= queue.length) return cb() // 表示钩子执行完毕,直接调用回调函数完成渲染
const hook = queue[index]
iterator(hook, () => {
next(index + 1)
})
}
next(0)
}
export default class History {
// ...
// 根据路径渲染组件,数据变化更新视图
transitionTo(location, onComplete) { // 默认会先执行一次
// ...
let queue = [].concat(this.router.beforeEachHooks)
const iterator = (hook, next) => {
// hook:用户传入的回调函数
// next函数,用于调用下一个回调
hook(route, this.current, next)
}
// 依次执行队列,执行完毕后更新路由
runQueue(queue, iterator, () => {
this.updateRoute(route)
onComplete && onComplete() // onComplete调用后路由变化会再次调用transitionTo
})
// ...
}
// ...
}