路由注册

Vue 从它的设计上就是一个渐进式 JavaScript 框架,它本身的核心是解决视图渲染的问题,其它的能力就通过插件的方式来解决。Vue-Router 就是官方维护的路由插件,在介绍它的注册实现之前,我们先来分析一下 Vue 通用的插件注册原理。

Vue.use

Vue 提供了 Vue.use 的全局 API 来注册这些插件,所以我们先来分析一下它的实现原理,定义在 vue/src/core/global-api/use.js 中:

  1. export function initUse (Vue: GlobalAPI) {
  2. Vue.use = function (plugin: Function | Object) {
  3. const installedPlugins = (this._installedPlugins || (this._installedPlugins = []))
  4. if (installedPlugins.indexOf(plugin) > -1) {
  5. return this
  6. }
  7. const args = toArray(arguments, 1)
  8. args.unshift(this)
  9. if (typeof plugin.install === 'function') {
  10. plugin.install.apply(plugin, args)
  11. } else if (typeof plugin === 'function') {
  12. plugin.apply(null, args)
  13. }
  14. installedPlugins.push(plugin)
  15. return this
  16. }
  17. }

Vue.use 接受一个 plugin 参数,并且维护了一个 _installedPlugins 数组,它存储所有注册过的 plugin;接着又会判断 plugin 有没有定义 install 方法,如果有的话则调用该方法,并且该方法执行的第一个参数是 Vue;最后把 plugin 存储到 installedPlugins 中。

可以看到 Vue 提供的插件注册机制很简单,每个插件都需要实现一个静态的 install 方法,当我们执行 Vue.use 注册插件的时候,就会执行这个 install 方法,并且在这个 install 方法的第一个参数我们可以拿到 Vue 对象,这样的好处就是作为插件的编写方不需要再额外去import Vue 了。

路由安装

Vue-Router 的入口文件是 src/index.js,其中定义了 VueRouter 类,也实现了 install 的静态方法:VueRouter.install = install,它的定义在 src/install.js 中。

  1. export let _Vue
  2. export function install (Vue) {
  3. if (install.installed && _Vue === Vue) return
  4. install.installed = true
  5. _Vue = Vue
  6. const isDef = v => v !== undefined
  7. const registerInstance = (vm, callVal) => {
  8. let i = vm.$options._parentVnode
  9. if (isDef(i) && isDef(i = i.data) && isDef(i = i.registerRouteInstance)) {
  10. i(vm, callVal)
  11. }
  12. }
  13. Vue.mixin({
  14. beforeCreate () {
  15. if (isDef(this.$options.router)) {
  16. this._routerRoot = this
  17. this._router = this.$options.router
  18. this._router.init(this)
  19. Vue.util.defineReactive(this, '_route', this._router.history.current)
  20. } else {
  21. this._routerRoot = (this.$parent && this.$parent._routerRoot) || this
  22. }
  23. registerInstance(this, this)
  24. },
  25. destroyed () {
  26. registerInstance(this)
  27. }
  28. })
  29. Object.defineProperty(Vue.prototype, '$router', {
  30. get () { return this._routerRoot._router }
  31. })
  32. Object.defineProperty(Vue.prototype, '$route', {
  33. get () { return this._routerRoot._route }
  34. })
  35. Vue.component('RouterView', View)
  36. Vue.component('RouterLink', Link)
  37. const strats = Vue.config.optionMergeStrategies
  38. strats.beforeRouteEnter = strats.beforeRouteLeave = strats.beforeRouteUpdate = strats.created
  39. }

当用户执行 Vue.use(VueRouter) 的时候,实际上就是在执行 install 函数,为了确保 install 逻辑只执行一次,用了 install.installed 变量做已安装的标志位。另外用一个全局的 _Vue 来接收参数 Vue,因为作为 Vue 的插件对 Vue 对象是有依赖的,但又不能去单独去 import Vue,因为那样会增加包体积,所以就通过这种方式拿到 Vue 对象。

Vue-Router 安装最重要的一步就是利用 Vue.mixin 去把 beforeCreatedestroyed 钩子函数注入到每一个组件中。Vue.mixin 的定义,在 vue/src/core/global-api/mixin.js 中:

  1. export function initMixin (Vue: GlobalAPI) {
  2. Vue.mixin = function (mixin: Object) {
  3. this.options = mergeOptions(this.options, mixin)
  4. return this
  5. }
  6. }

它的实现实际上非常简单,就是把要混入的对象通过 mergeOption 合并到 Vueoptions 中,由于每个组件的构造函数都会在 extend 阶段合并 Vue.options 到自身的 options 中,所以也就相当于每个组件都定义了 mixin 定义的选项。

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

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

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

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

最后定义了路由中的钩子函数的合并策略,和普通的钩子函数一样。

总结

那么到此为止,我们分析了 Vue-Router 的安装过程,Vue 编写插件的时候通常要提供静态的 install 方法,我们通过 Vue.use(plugin) 时候,就是在执行 install 方法。Vue-Routerinstall 方法会给每一个组件注入 beforeCreatedestoryed 钩子函数,在 beforeCreate 做一些私有属性定义和路由初始化工作,下一节我们就来分析一下 VueRouter 对象的实现和它的初始化工作。

原文: https://ustbhuangyi.github.io/vue-analysis/vue-router/install.html