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

結語:

每一次總結,都是對之前讀源碼的再一次深入的瞭解

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章