源码结构

跟上一章节讲到的 vuex 一样,vue router 的源码在src目录下:

├── components                   // 两个组件router-link和router-view
│   ├── link.js
│   └── view.js
├── create-matcher.js            // route的匹配
├── create-route-map.js          // route的映射
├── history                      // route的三种方式,默认的是hash
│   ├── abstract.js
│   ├── base.js
│   ├── hash.js
│   └── html5.js
├── index.js                     // 定义了VueRouter类,也是VueRouter源码的入口
├── install.js                   // router 的安装 Vue.use(VueRouter)之后执行的install方法
└── util                         // 功能类和功能函数
    ├── async.js
    ├── dom.js
    ├── location.js
    ├── misc.js
    ├── params.js
    ├── path.js
    ├── push-state.js
    ├── query.js
    ├── resolve-components.js
    ├── route.js
    ├── scroll.js
    └── warn.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

vue router 注册

对于路由注册来说,核心就是调用了 Vue.use(VueRouter),使得 VueRouter 可以使用 Vue。然后 Vue 来调用 VueRouter 的install函数。而 install 函数的核心就是给组件混入钩子函数和全局注册两个路由组件。接下来我们就看一下 install 函数主要做了什么事情。先看一下源码(在src/install.js中):

TIP

这个地方在上一章讲解 vuex 的时候也提到了,Vue 中使用插件就是调用 Vue.use()方法。如果传入的参数有 install 方法,则调用插件的 install 方法,如果传入的参数本身是一个 function,则直接执行。在这里我们不做过多讲解,如果需要可以去读一下 vue 的源码。

路由安装

install 函数定义在src/install.js中:

export let _Vue;

export function install(Vue) {
    // 为了确保install逻辑只执行了一次,所以用install.installed做已安装的标识
    if (install.installed && _Vue === Vue) return;
    install.installed = true;

    _Vue = Vue; //这么做是因为Vue的插件对Vue依赖,但是用不能单独import 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-router安装最重要的一步,就是利用Vue.mixin把beforeCreate和destroyed钩子函数注入到每个组件中
    Vue.mixin({
        beforeCreate() {
            // 判断组件是否存在router对象,该对象只在根组件上有
            if (isDef(this.$options.router)) {
                this._routerRoot = this; //this._routerRoot表示vue自身
                this._router = this.$options.router; //this._router表示VueRouter的router实例,在new Vue的时候传入的
                this._router.init(this); //初始化router
                Vue.util.defineReactive(
                    //把this._route变成响应式对象
                    this,
                    "_route",
                    this._router.history.current
                );
            } else {
                // 用于router-view层级判断
                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); //定义全局组件router-view
    Vue.component("RouterLink", Link); //定义全局组件router-link

    const strats = Vue.config.optionMergeStrategies;
    // use the same hook merging strategy for route hooks
    strats.beforeRouteEnter = strats.beforeRouteLeave = strats.beforeRouteUpdate =
        strats.created;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67

通过以上代码,我们可以看出install函数主要做了以下几件事情:

  • 利用 Vue.mixin 把 beforeCreate 和 destroyed 钩子函数注入到每个组件中
  • 根组件首次进入的时候,初始化路由 this._router.init(),然后利用 defineReactive 把 this._route 变成响应式对象,this._routerRoot 始终指向根组件
  • 在 Vue 的原型上定义了$router和$route2 个属性的 get 方法,方便以后的访问
  • 注册 router-view 和 router-link 两个组件

在注册完 VueRouter 之后,我们回去创建 VueRouter 的实例。那么我们就看一下 VueRouter 的实例化。

支付宝
微信