• 路由注册
    • Vue.use
    • 路由安装
    • 总结

    路由注册

    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