text: /* @flow */
  
  import { install } from './install'
  import { START } from './util/route'
  import { assert } from './util/warn'
  import { inBrowser } from './util/dom'
  import { cleanPath } from './util/path'
  import { createMatcher } from './create-matcher'
  import { normalizeLocation } from './util/location'
  import { supportsPushState } from './util/push-state'
  
  import { HashHistory } from './history/hash'
  import { HTML5History } from './history/html5'
  import { AbstractHistory } from './history/abstract'
  
  import type { Matcher } from './create-matcher'
  
  /**
   * 路由对象
   */
  export default class VueRouter {
      static install: () => void;
      static version: string;
  
      app: any;
      apps: Array < any > ;
      ready: boolean;
      readyCbs: Array < Function > ;
      options: RouterOptions;
      mode: string;
      history: HashHistory | HTML5History | AbstractHistory;
      matcher: Matcher;
      fallback: boolean;
      beforeHooks: Array < ? NavigationGuard > ;
      resolveHooks: Array < ? NavigationGuard > ;
      afterHooks: Array < ? AfterNavigationHook > ;
  
  
      constructor(options: RouterOptions = {}) {
          this.app = null
          this.apps = []
          this.options = options
          this.beforeHooks = []
          this.resolveHooks = []
          this.afterHooks = []
          this.matcher = createMatcher(options.routes || [], this)
  
          // 默认hash模式
          let mode = options.mode || 'hash'
  
          // 是否降级处理
          this.fallback = mode === 'history' && !supportsPushState && options.fallback !== false
  
          // 进行降级处理
          if (this.fallback) {
              mode = 'hash'
          }
  
          if (!inBrowser) {
              mode = 'abstract'
          }
          this.mode = mode
  
          // 根据不同的mode进行不同的处理
          switch (mode) {
              case 'history':
                  this.history = new HTML5History(this, options.base)
                  break
              case 'hash':
                  this.history = new HashHistory(this, options.base, this.fallback)
                  break
              case 'abstract':
                  this.history = new AbstractHistory(this, options.base)
                  break
              default:
                  if (process.env.NODE_ENV !== 'production') {
                      assert(false, `invalid mode: ${mode}`)
                  }
          }
      }
  
      // 获取匹配的路由对象
      match(
          raw: RawLocation,
          current ? : Route,
          redirectedFrom ? : Location
      ): Route {
          return this.matcher.match(raw, current, redirectedFrom)
      }
  
      // 获取当前的路由
      get currentRoute(): ? Route {
          return this.history && this.history.current
      }
  
      // 初始化
      init(app: any /* Vue component instance */ ) {
          process.env.NODE_ENV !== 'production' && assert(
              install.installed,
              `not installed. Make sure to call \`Vue.use(VueRouter)\` ` +
              `before creating root instance.`
          )
  
          this.apps.push(app)
  
          // main app already initialized.
          if (this.app) {
              return
          }
  
          this.app = app
  
          const history = this.history
  
          if (history instanceof HTML5History) {
              // history模式下
              history.transitionTo(history.getCurrentLocation())
          } else if (history instanceof HashHistory) {
              // hash 模式下
              const setupHashListener = () => {
                  history.setupListeners()
              }
              history.transitionTo(
                  history.getCurrentLocation(),
                  setupHashListener,
                  setupHashListener
              )
          }
  
          history.listen(route => {
              this.apps.forEach((app) => {
                  app._route = route
              })
          })
      }
  
      // 全局守护
      beforeEach(fn: Function): Function {
          return registerHook(this.beforeHooks, fn)
      }
  
      // 全局守护
      beforeResolve(fn: Function): Function {
          return registerHook(this.resolveHooks, fn)
      }
  
      // 全局守护
      afterEach(fn: Function): Function {
          return registerHook(this.afterHooks, fn)
      }
  
  
      onReady(cb: Function, errorCb ? : Function) {
          this.history.onReady(cb, errorCb)
      }
  
      onError(errorCb: Function) {
          this.history.onError(errorCb)
      }
  
      push(location: RawLocation, onComplete ? : Function, onAbort ? : Function) {
          this.history.push(location, onComplete, onAbort)
      }
  
      replace(location: RawLocation, onComplete ? : Function, onAbort ? : Function) {
          this.history.replace(location, onComplete, onAbort)
      }
  
      go(n: number) {
          this.history.go(n)
      }
  
      back() {
          this.go(-1)
      }
  
      forward() {
          this.go(1)
      }
  
      // 获取
      getMatchedComponents(to ? : RawLocation | Route): Array < any > {
          const route: any = to ?
              to.matched ?
              to :
              this.resolve(to).route : this.currentRoute
          if (!route) {
              return []
          }
          return [].concat.apply([], route.matched.map(m => {
              return Object.keys(m.components).map(key => {
                  return m.components[key]
              })
          }))
      }
  
      resolve(
          to: RawLocation,
          current ? : Route,
          append ? : boolean
      ): {
          location: Location,
          route: Route,
          href: string,
          // for backwards compat
          normalizedTo: Location,
          resolved: Route
      } {
          const location = normalizeLocation(
              to,
              current || this.history.current,
              append,
              this
          )
          const route = this.match(location, current)
          const fullPath = route.redirectedFrom || route.fullPath
          const base = this.history.base
          const href = createHref(base, fullPath, this.mode)
          return {
              location,
              route,
              href,
              // for backwards compat
              normalizedTo: location,
              resolved: route
          }
      }
  
      addRoutes(routes: Array < RouteConfig > ) {
          this.matcher.addRoutes(routes)
          if (this.history.current !== START) {
              this.history.transitionTo(this.history.getCurrentLocation())
          }
      }
  }
  
  // 注册钩子方法，返回一个注销的函数
  function registerHook(list: Array < any > , fn: Function): Function {
      list.push(fn)
      return () => {
          const i = list.indexOf(fn)
          if (i > -1) list.splice(i, 1)
      }
  }
  
  // 创建 href
  function createHref(base: string, fullPath: string, mode) {
      var path = mode === 'hash' ? '#' + fullPath : fullPath
      return base ? cleanPath(base + '/' + path) : path
  }
  
  VueRouter.install = install
  VueRouter.version = '__VERSION__'
  
  if (inBrowser && window.Vue) {
      window.Vue.use(VueRouter)
  }
  