# Vue-Router3.x 源码概览

Vue Router 官网 (opens new window)

# 用户代码

router/index.js

import Vue from "vue"
import Router from "vue-router"
Vue.use(Router)
export default new Router({
  routes: [
    {
      path: "/",
      redirect: {
        path: "/message",
      },
    },
  ],
})
1
2
3
4
5
6
7
8
9
10
11
12
13

# Vue 加载 VueRouter 插件

当执行Vue.use(Router)时,实际上在 vue 内部会调用Router上的install函数

Vue.use = function(plugin) {
  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
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

install执行时首先会调用Vue.mixinbeforeCreatedestroyed钩子函数注入到每一个组件中

在 Vue 原型上定义了 $router 和 $route 2 个属性的 get ⽅法

⼜通过 Vue.component ⽅法定义了全局的 <router-link><router-view> 2 个组件

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
  // 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

# 实例化 Vue Router

  • VueRouter 类
class VueRouter {
  constructor (options = {}) {
    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:
        {
          assert(false, `invalid mode: ${mode}`);
        }
    }
  }

  match (raw, current, redirectedFrom) {
    return this.matcher.match(raw, current, redirectedFrom)
  }

  get currentRoute () {
    return this.history && this.history.current
  }

  init (app) { // 初始化阶段
    ...
  }

  beforeEach (fn) {
    return registerHook(this.beforeHooks, fn)
  }

  beforeResolve (fn) {
    return registerHook(this.resolveHooks, fn)
  }

  afterEach (fn) {
    return registerHook(this.afterHooks, fn)
  }

  onReady (cb, errorCb) {
    this.history.onReady(cb, errorCb);
  }

  onError (errorCb) {
    this.history.onError(errorCb);
  }

  push (location, onComplete, onAbort) {
    // $flow-disable-line
    if (!onComplete && !onAbort && typeof Promise !== 'undefined') {
      return new Promise((resolve, reject) => {
        this.history.push(location, resolve, reject);
      })
    } else {
      this.history.push(location, onComplete, onAbort);
    }
  }

  replace (location, onComplete, onAbort) {
    // $flow-disable-line
    if (!onComplete && !onAbort && typeof Promise !== 'undefined') {
      return new Promise((resolve, reject) => {
        this.history.replace(location, resolve, reject);
      })
    } else {
      this.history.replace(location, onComplete, onAbort);
    }
  }

  go (n) {
    this.history.go(n);
  }

  back () {
    this.go(-1);
  }

  forward () {
    this.go(1);
  }

  getMatchedComponents (to) {
    const route = to
      ? to.matched
        ? to
        : this.resolve(to).route
      : this.currentRoute;
    if (!route) {
      return []
    }
    return [].concat.apply(
      [],
      route.matched.map(m => {
        return Object.keys(m.components).map(key => {
          return m.components[key]
        })
      })
    )
  }

  resolve (
    to,
    current,
    append
  ) {
    current = current || this.history.current;
    const location = normalizeLocation(to, current, append, this);
    const route = this.match(location, current);
    const fullPath = route.redirectedFrom || route.fullPath;
    const base = this.history.base;
    const href = createHref(base, fullPath, this.mode);
    return {
      location,
      route,
      href,
      // for backwards compat
      normalizedTo: location,
      resolved: route
    }
  }

  addRoutes (routes) {
    this.matcher.addRoutes(routes);
    if (this.history.current !== START) {
      this.history.transitionTo(this.history.getCurrentLocation());
    }
  }
}
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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151

# 使用 Router

上次更新: 2022/2/24 上午12:11:26