Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

vue-router 实现 -- new VueRouter(options) #24

Open
muwoo opened this issue May 30, 2018 · 0 comments
Open

vue-router 实现 -- new VueRouter(options) #24

muwoo opened this issue May 30, 2018 · 0 comments

Comments

@muwoo
Copy link
Owner

muwoo commented May 30, 2018

为了构造出 router 对象,我们还需要对VueRouter进行实例化的操作,比如这样:

const router = new VueRouter({
  mode: 'history',
  routes: [
    { path: '/', name: 'home', component: Home },
    { path: '/foo', name: 'foo', component: Foo },
    { path: '/bar/:id', name: 'bar', component: Bar }
  ]
})

constructor

我们来看一下在VueRouter内部的源码定义:

export default class VueRouter {
 
  // ...
  constructor (options: RouterOptions = {}) {
    this.app = null
    this.apps = []
    this.options = options
    this.beforeHooks = []
    this.resolveHooks = []
    this.afterHooks = []
    this.matcher = createMatcher(options.routes || [], this)

    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

    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 () {}
  beforeEach () {}
  beforeResolve () {}
  afterEach () {}
  onReady () {}
  onError () {}
  push () {}
  replace () { }
  go () {}
  back () { }
  forward () { }
  getMatchedComponents () { }
  resolve ( ) { }
  addRoutes () { }
}

这里我们忽略了大部分的函数实现,后面我么再展开来看。先来看一下constructor实例化的时候将会做的处理:通过new VueRouter({...})我们创建了一个 VueRouter 的实例。VueRouter中通过参数mode来指定路由模式,前面已经简单的了解了一下前端路由的2种模式。通过上面的代码,我们可以看出来 VueRouter对不同模式的实现大致是这样的:

  1. 首先根据mode来确定所选的模式,如果当前环境不支持history模式,会强制切换到hash模式;
  2. 如果当前环境不是浏览器环境,会切换到abstract模式下。然后再根据不同模式来生成不同的history操作对象。

由于上篇文章已经介绍了在 install 的过程中,会执行改对象的 init 函数。我们接下来的主要任务就是分析init 的实现。

init

  init (app: any /* Vue component 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.transitionTo(history.getCurrentLocation())
    } else if (history instanceof HashHistory) {
      const setupHashListener = () => {
        history.setupListeners()
      }
      history.transitionTo(
        history.getCurrentLocation(),
        setupHashListener,
        setupHashListener
      )
    }

    history.listen(route => {
      this.apps.forEach((app) => {
        app._route = route
      })
    })
  }

回顾一下在 inistall 的 beforCreate 钩子内,我们通过这种方式调用了实例的init方法:

this._router.init(this)

然后我们来分析一下执行的大致过程:init 方法内的 app变量便是存储的当前的vue实例的this。然后将 app 存入数组apps中。通过this.app判断是实例否已经被初始化。然后通过history来确定不同路由的切换动作动作 history.transitionTo。最后通过 history.listen来注册路由变化的响应回调。
接下来我们就要了解一下 history.transitionTo的主要流程以及 history.listen的实现。当然最基础的是先明白history是个什么东西。接下来我们会分别介绍不同mode下的 history 的实现。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant