# 深入浅出 VueRouter 原理
# install 方法
作为vue插件,为了Vue.use 函数可以用,作为vue插件必须含有install方法

install的第一个参数就是Vue构造函数,install的主要作用就是为了每个vue实例全局注册router和route对象。并且在内部实现全局的RouterView和RouteLink组件- 通过
Vue.mixin()将mixin里面的内容全局混入到每个vue实例对象上的options里面 beforeCreate确保在vue实例对象options初始化没完成前注入mixin里面的内容this.$options.router可以用来判断是否为根组件,只有根组件的options里面才会有router参数- 如果不是根组件,找其父组件的
_routerRoot。因为父组件的beforeCreate先执行。因此可以确保子组件的beforeCreate生命周期函数里面可以拿到父组件的_routerRoot - 为
Vue添加$router,$route
TIP
- 当用户执行
Vue.use(VueRouter)的时候,实际上就是在执行install函数,为了确保install逻辑只执行一次,用了install.installed变量做已安装的标志位。另外用一个全局的_Vue来接收参数Vue,因为作为Vue的插件对Vue对象是有依赖的,但又不能去单独去import Vue,因为那样会增加包体积,所以就通过这种方式拿到Vue对象。 mixin的beforeCreate钩子函数,对于根Vue实例而言,执行该钩子函数时定义了this._routerRoot表示它自身;this._router表示VueRouter的实例router,它是在new Vue的时候传入的;另外执行了this._router.init()方法初始化router。然后用defineReactive方法把this._route变成响应式对象。- 而对于子组件而言,由于组件是树状结构,在遍历组件树的过程中,它们在执行该钩子函数的时候
this._routerRoot始终指向的离它最近的传入了router对象作为配置而实例化的父实例。 - 接着给
Vue原型上定义了$router和$route2个属性的get方法,这就是为什么我们可以在组件实例上可以访问this.$router以及this.$route
# new VueRouter 做了哪些事情
constructor (options: RouterOptions = {}) {
if (process.env.NODE_ENV !== 'production') {
warn(this instanceof VueRouter, `Router must be called with the new operator.`)
}
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:
if (process.env.NODE_ENV !== 'production') {
assert(false, `invalid mode: ${mode}`)
}
}
}
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
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
TIP
- 构造函数定义了一些属性,其中
this.app表示根Vue实例,this.apps保存持有$options.router属性的Vue实例,this.options保存传入的路由配置,this.beforeHooks、this.resolveHooks、this.afterHooks表示一些钩子函数 this.matcher表示路由匹配器this.fallback表示在浏览器不支持history.pushState的情况下,根据传入的fallback配置参数,决定是否回退到hash模式this.mode表示路由创建的模式,this.history表示路由历史的具体的实现实例,它是根据this.mode的不同实现不同,它有History基类,然后不同的history实现都是继承History。
# RouterView组件

- 用
parent的createElement用来渲染组件 depth路由嵌套层级const matched = route.matched[depth]根据路由嵌套层级获取到匹配到需要渲染的组件对象const component = cache[name] = matched.components[name]需要渲染的组件h(component, data, children)渲染组件
RouterView本质上为通过this.$route获取当前激活的路由。并通过路由配置获取当前激活路由对应的组件进行渲染。
# RouterLink 组件
RouterLink本质将其渲染成a标签进行渲染
# 路由模式
# hash模式
路由主要包括hash/history模式,还有一个Node环境下使用的abstract模式
export class HashHistory extends History {
constructor (router: Router, base: ?string, fallback: boolean) {
super(router, base)
// check history fallback deeplinking
if (fallback && checkFallback(this.base)) {
return
}
ensureSlash()
}
}
function checkFallback (base) {
const location = getLocation(base)
if (!/^\/#/.test(location)) {
window.location.replace(cleanPath(base + '/#' + location))
return true
}
}
function ensureSlash (): boolean {
const path = getHash()
if (path.charAt(0) === '/') {
return true
}
replaceHash('/' + path)
return false
}
export function getHash (): string {
// We can't use window.location.hash here because it's not
// consistent across browsers - Firefox will pre-decode it!
let href = window.location.href
const index = href.indexOf('#')
// empty path
if (index < 0) return ''
href = href.slice(index + 1)
return href
}
function replaceHash (path) {
if (supportsPushState) {
replaceState(getUrl(path))
} else {
window.location.replace(getUrl(path))
}
}
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
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
fallback:当浏览器不支持history.pushState控制路由是否应该回退到hash模式。默认值为true。- 通过
checkFallback检测本来为history但是需要被转换成hash模式的情况 getHash返回#后面的hash部分。replaceHash如果path后面没有/则自动进行追加hash模式通过监听hashchange事件实现视图更新,对于支持history模式的则监听popstate事件实现视图更新

# history模式
通过监听popstate