vue-router之history类浅析

当前版本: 3.0.3
类目录: src/history/base.js

前言:

对于vue-router来说,有三种路由模式history,hash,abstract, abstract是运行在没有window的环境下的,这三种模式都是继承于history类,history实现了一些共用的方法,对于一开始看vue-router源码来说,可以从这里开始看起。

初始属性

router: Router;  表示VueRouter实例。实例化History类时的第一个参数
  base: string;    表示基路径。会用normalizeBase进行规范化。实例化History类时的第二个参数。
  current: Route;  表示当前路由(route)。
  pending: ?Route; 描述阻塞状态。
  cb: (r: Route) => void;  监听时的回调函数。
  ready: boolean;  描述就绪状态。
  readyCbs: Array<Function>; 就绪状态的回调数组。
  readyErrorCbs: Array<Function>;  就绪时产生错误的回调数组。
  errorCbs: Array<Function>;  错误的回调数组

  // implemented by sub-classes
  <!-- 下面几个是需要子类实现的方法,这里就先不说了,之后写其他类实现的时候分析 -->
  +go: (n: number) => void;
  +push: (loc: RawLocation) => void;
  +replace: (loc: RawLocation) => void;
  +ensureURL: (push?: boolean) => void;
  +getCurrentLocation: () => string;

对于history类来说,主要是下下面两个函数的逻辑

transitionTo

这个方法主要是对路由跳转的封装, location接收的是HTML5History,HashHistory,AbstractHistory, onComplete是成功的回调,onAbort是失败的回调

transitionTo (location: RawLocation, onComplete?: Function, onAbort?: Function) {
    const route = this.router.match(location, this.current)  // 解析成每一个location需要的route
    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) })
      }
    })
  }

confirmTransition

这是方法是确认跳转,route是匹配的路由对象, onComplete是匹配成功的回调, 是匹配失败的回调

confirmTransition(route: Route, onComplete: Function, onAbort?: Function) {
        const current = this.current
        const abort = err => {  // 异常处理函数
            if (isError(err)) {
                if (this.errorCbs.length) {
                    this.errorCbs.forEach(cb => { cb(err) })
                } else {
                    warn(false, 'uncaught error during route navigation:')
                    console.error(err)
                }
            }
            onAbort && onAbort(err)
        }
        if (
            isSameRoute(route, current) &&
            // in the case the route map has been dynamically appended to
            route.matched.length === current.matched.length
        ) {
            this.ensureURL()
            return abort()
        }
        <!-- 根据当前路由对象和匹配的路由:返回更新的路由、激活的路由、停用的路由 -->
        const {
            updated,
            deactivated,
            activated
        } = resolveQueue(this.current.matched, route.matched)
        <!-- 需要执行的任务队列 -->
        const queue: Array<?NavigationGuard> = [].concat(
            // beforeRouteLeave 钩子函数
            extractLeaveGuards(deactivated),
            // 全局的beforeHooks勾子
            this.router.beforeHooks,
            // beforeRouteUpdate 钩子函数调用
            extractUpdateHooks(updated),
            // config里的勾子
            activated.map(m => m.beforeEnter),
            // async components
            resolveAsyncComponents(activated)
        )
        
        this.pending = route
        <!-- 对于queue数组所执行的迭代器方法 -->
        const iterator = (hook: NavigationGuard, next) => {
            if (this.pending !== route) {
                return abort()
            }
            try {
                hook(route, current, (to: any) => {
                    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)
            }
        }
        
        runQueue(queue, iterator, () => {
            const postEnterCbs = []
            const isValid = () => this.current === route
            <!-- beforeRouteEnter 钩子函数调用 -->
            const enterGuards = extractEnterGuards(activated, postEnterCbs, isValid)
            const queue = enterGuards.concat(this.router.resolveHooks)
            <!-- 迭代运行queue -->
            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() })
                    })
                }
            })
        })
    }

结语:

每一次总结,都是对之前读源码的再一次深入的了解

    原文作者:fuqihan
    原文地址: https://segmentfault.com/a/1190000019238946
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞