Skip to content

Vue.use

使用vue-router的第一步是注册路由插件Vue.use(VueRouter),Vue 提供了 Vue.use 的全局 API 来注册插件,定义在 vue/src/core/global-api/use.js 中:

js
export function initUse (Vue: GlobalAPI) {
  Vue.use = function (plugin: Function | Object) {
    const installedPlugins = (this._installedPlugins || (this._installedPlugins = []))
    if (installedPlugins.indexOf(plugin) > -1) {
      return this
    }

    const args = toArray(arguments, 1)
    args.unshift(this)
    if (typeof plugin.install === 'function') {
      plugin.install.apply(plugin, args)
    } else if (typeof plugin === 'function') {
      plugin.apply(null, args)
    }
    installedPlugins.push(plugin)
    return this
  }
}

可以看到Vue.use接收一个plugin参数,首先查看是否已经注册过插件,即使用数组installedPlugins维护,如果还未注册过则执行插件中实现的install方法,最后将插件添加到installedPlugins中。

Vue.install

Vue.install静态方法定义在src/install.js中,核心在于使用了Vue.mixin注入了beforeCreate 和 destroyed 这两个钩子函数

js
export let _Vue
export function install (Vue) {
  if (install.installed && _Vue === Vue) return
  install.installed = true

  _Vue = Vue

  const isDef = v => v !== undefined

  const registerInstance = (vm, callVal) => {
    let i = vm.$options._parentVnode
    if (isDef(i) && isDef(i = i.data) && isDef(i = i.registerRouteInstance)) {
      i(vm, callVal)
    }
  }

  Vue.mixin({
    beforeCreate () {
      if (isDef(this.$options.router)) {
        this._routerRoot = this
        this._router = this.$options.router
        this._router.init(this)
        Vue.util.defineReactive(this, '_route', this._router.history.current)
      } else {
        this._routerRoot = (this.$parent && this.$parent._routerRoot) || this
      }
      registerInstance(this, this)
    },
    destroyed () {
      registerInstance(this)
    }
  })

  Object.defineProperty(Vue.prototype, '$router', {
    get () { return this._routerRoot._router }
  })

  Object.defineProperty(Vue.prototype, '$route', {
    get () { return this._routerRoot._route }
  })

  Vue.component('RouterView', View)
  Vue.component('RouterLink', Link)

  const strats = Vue.config.optionMergeStrategies
  strats.beforeRouteEnter = strats.beforeRouteLeave = strats.beforeRouteUpdate = strats.created
}

首先需要确保install只执行一次,同时存储了一次vue对象。其中核心步骤是Vue.mixin 去把 beforeCreate 和 destroyed 钩子函数注入到每一个组件中。

先看混入的 beforeCreate 钩子函数,对于根 Vue 实例而言,执行该钩子函数时定义了 this._routerRoot 表示它自身;this._router 表示 VueRouter 的实例 router,它是在 new Vue 的时候传入的;另外执行了 this._router.init() 方法初始化 router,这个逻辑之后介绍,然后用 defineReactive 方法把 this._route 变成响应式对象。而对于子组件而言,由于组件是树状结构,在遍历组件树的过程中,它们在执行该钩子函数的时候 this._routerRoot 始终指向离它最近的传入了 router 对象作为配置而实例化的父实例。

对于 beforeCreate 和 destroyed 钩子函数,它们都会执行 registerInstance 方法,这个方法的作用我们也是之后会介绍。

接着给 Vue 原型上定义了$router$route 2 个属性的 get 方法,这就是为什么我们可以在组件实例上可以访问this.$router 以及 this.$route,它们的作用之后介绍。

接着又通过 Vue.component 方法定义了全局的 <router-link><router-view> 两个组件,这也是为什么我们在写模板的时候可以使用这两个标签,它们的作用也是之后介绍。