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
    })
    // ...
  }
  // ...
}