Home  >  Article  >  Web Front-end  >  Take you through the navigation guard of vue-Router

Take you through the navigation guard of vue-Router

不言
不言forward
2018-09-30 15:58:343936browse

The content of this article is about taking you through the navigation guard of vue-Router. It has certain reference value. Friends in need can refer to it. I hope it will be helpful to you.

vue-router Navigation Guard

In this article, I will sort out the following things for you,

1: What is the execution order of the navigation guard? Like?

2: What is the use of next in the navigation guard?

3: Why does the afterEach guard not have next?

4: Can beforeEach be superimposed?

5: What parts does routing jump go through?

The history attribute of a content router instance mentioned before helps us do all the jump parts, so the content of the navigation guard is also in the history.

Take you through the navigation guard of vue-Router

Let’s take a look at this push method using the HTML5History class,

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 when we jump. push is this method), transitionTo is called to complete a series of jump content, but this method does not exist in the HTML5 class. The method
transitionTo inherited from the base.js class is to implement routing jumps. Method
The main process of transitionTo is a combination of the confirmTranstion method and the uodateRoute method. Translated into Mandarin: the route jump must first go through a confirmation jump process, and then perform a route update operation after the confirmation process is completed.

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

confirmTransitonWhat did it do? First determine whether you are on the same route. If so, then do nothing. In the second step, we have to start collecting a wave of guards, then collect the guards, and then execute each guard once. The confirmTransition will be executed successfully.

The following are some screenshots of the source code:

Take you through the navigation guard of vue-Router

What are the difficulties in this process?

How to collect guards to form a guard queue?

How to execute guards in sequence while stopping the guard queue at any time?

How to find the node after the guard queue is executed (you can notify us after the guard queue is executed)

A runQueue function is encapsulated in vue-router to solve the above three problems. two. The first question involves a large chapter of vue-router processing routing. Let’s focus on the runQueue function

The idea of ​​​​runQueue function:

1: Iterator mode to ensure traversal of the queue Each step is controllable.

2: Execute the corresponding callback function after the queue is completed.

Infer the corresponding functions of the function parameters:

queue: needs to be executed Guard queue

fn: Iterator function, each guard in the guard queue executes the second parameter of the iterator function

fn to make the iterator enter the next step. If it is not used, Will not go to the next step (very important)

cb: The callback function called at the end

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 is enough to help us solve the problem of guard queue processing.

(Hurry up and save it, this function is awesome!)

We have made the big hammer to handle the guard queue and can start working on it. What about your guard queue? ?

Yes, yes, there are still guard queues to collect.
At this time we have to think about what guards are there?

There are two types of guards: front guards and rear guards.

  • Pre-guard:

  1. Global pre-guard: beforeEach beforeResolve

  2. Routing exclusive guards: beforeEnter

  3. ##Guards within the component: beforeRouterEnter, beforeRouterUpdate, beforeRouteLeave

##Post guard:
    1. Global post-guard: afterEach
    We need to think about how these guards are registered,

      Registered in the routing instance:
    • beforeEach, beforeResolve, afterEach

    • Registered in the routing configuration (route exclusive guard ):
    • beforeEnter

    • Route guards within the component:
    • beforeRouteLeave, beforeRouteUpdate, beforeRouteEnter

    • Okay, we are going to extract the corresponding guard.

    The guards of confirmTransition are divided into two queues: Let’s first look at the first queue

     // 拿到路由跳转中更新、摧毁、激活时对应展示的组件。
     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)
        )

    The order of a queue:

      If you get the destroyed component, squeeze out all the leaving guards in the component.
    1. Global beforeEach component.
    2. Get all updated components and extract the update guards in all components.
    3. Traverse the routes you want to enter and get the exclusive guards of all routes.
    4. Load the asynchronous component to be activated
    5. 4 of the 7 guards are taken out in order and put in The first queue.

    The next step is to have an iterator to handle the guard:

    我们该如何处理守卫?

    1. 保证在守卫中可以停止并且跳转到其余路由,

    2. 保证守卫可以正常通过,

    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:

    1. 收集了被的激活组件内的进入守卫

    2. 全局的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)
      }
    }</any>

    这段代码beforeEach是通过注册守卫的方式,将注册的全局前置守卫放在beforeHooks的容器内,这个容器里面装载着所有的前置守卫

    Take you through the navigation guard of vue-Router

    一家人(全局的 前置进入、前置resolve、后置守卫)整整齐齐的放在对应的容器里面,容器是个数组,所以注册全局守卫的时候,是支持注册多个的,

    router.beforeEach(()=>{xxx});
    router.beforeEach(()=>{yyy});
    // 这两个守卫都会执行,只是先注册的先执行,
    // registerHook这个方法还可以清除对应的守卫,这个方法也可以使用

    总结

    我们来回答一下开篇的5个问题

    1:导航守卫的执行顺序是怎么样的?

    beforeRouteLeave

    2:导航守卫中的next的用处?

    next的作用,使导航守卫队列的继续向下迭代

    3:为什么afterEach守卫没有next?

    afterEach根本不在导航守卫队列内,没有迭代的next

    4:beforeEach是否可以叠加?

    beforeEach是可以叠加的,所有的全局前置守卫按顺序存放在beforeHooks的数组里面,

    5:路由跳转经历了哪几部分?

    路由跳转的核心方法是transitionTo,在跳转过程中经历了一次confirmTransition,

    (beforeRouteLeave

    在第一个queue迭代完毕后,执行第二个(beforeRouteEnter

    在执行完毕后,开始执行updateRoute,之后执行全局的afterEach守卫。最后完成路由的跳转。

    5个问题解答完毕,希望对你的业务有帮助。


    The above is the detailed content of Take you through the navigation guard of vue-Router. For more information, please follow other related articles on the PHP Chinese website!

    Statement:
    This article is reproduced at:segmentfault.com. If there is any infringement, please contact admin@php.cn delete