# 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
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
install
执行时首先会调用Vue.mixin
将beforeCreate
和destroyed
钩子函数注入到每一个组件中
在 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
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
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