# Abstract模式
# 实例化
跟其他模式一样,都是在new VueRouter
的时候,通过判断传入的mode
来判断最后初始化路由模式
this.history = new AbstractHistory(this, options.base)
成功
上述代码可以看出,初始化的时候,会创建一个AbstractHistory
实例,将this
和设定的基准路径base
,我们找到AbstractHistory
的定义
export class AbstractHistory extends History { index: number stack: Array<Route> constructor (router: Router, base: ?string) { super(router, base) // 路由的历史记录 this.stack = [] // 当前路由的索引 this.index = -1 } }
成功
2
3
4
5
6
7
8
9
10
11
12
跟HTML5History
一样,都是继承自History
,并且在初始化的时候进行的调用,这里就不赘述History
的实现了。
接下来创建了两个变量,用于记录历史记录和当前的路由索引
# 初始化
在abstract
模式下,初始化的时候没有做额外操作,只是加个了路由变化的回调。
# 常见方法
分析完实例化和初始化后,我们分析下我们工作中一些常见的方法
# push
push
在VueRouter
类中的的实现跟其他模式是一致的,这里只分析不同的this.history.push
的实现
push (location: RawLocation, onComplete?: Function, onAbort?: Function) { this.transitionTo( location, route => { // 舍去当前索引之后的历史记录,将新路由添加到栈顶 this.stack = this.stack.slice(0, this.index + 1).concat(route) // 更新索引 this.index++ onComplete && onComplete(route) }, onAbort ) }
成功
2
3
4
5
6
7
8
9
10
11
12
13
可以看到,执行push
操作的时候,就是舍去当前索引之后的历史记录,将新路由添加到栈顶
有的同学可能有疑问,为什么不直接使用this.stack.push(route)
方法添加呢?
这是因为,还存在go
方法,当前索引可能不是栈的最顶端,当我们执行push
操作后,所有在当前历史记录后面的记录都应该被忽略。
如果我们直接使用this.stack.push(route)
,那么会直接把新的路由添加到历史记录栈的尾部,而忽略了可能存在的需要删除的历史记录,这样就可能导致历史记录错乱。因此,这里使用this.stack = this.stack.slice(0, this.index + 1).concat(route)
而非this.stack.push(route)
。
# replace
同理,replace
方法的不同点也是在于this.history.replace
的实现
replace (location: RawLocation, onComplete?: Function, onAbort?: Function) { this.transitionTo( location, route => { // 用新的记录替换当前记录,并舍去后续历史记录 this.stack = this.stack.slice(0, this.index).concat(route) onComplete && onComplete(route) }, onAbort ) }
成功
2
3
4
5
6
7
8
9
10
11
replace
方法就很清晰了,直接用新的记录替换当前记录,并舍去后续历史记录
# go
push
在VueRouter
类中的的实现跟其他模式是一致的,这里只分析不同的this.history.go
的实现
go (n: number) { const targetIndex = this.index + n // 目标索引超出范围,不予处理 if (targetIndex < 0 || targetIndex >= this.stack.length) { return } // 获取指向路由 const route = this.stack[targetIndex] // 跳转 this.confirmTransition( route, () => { const prev = this.current this.index = targetIndex this.updateRoute(route) // 触发跳转成功回调 this.router.afterHooks.forEach(hook => { hook && hook(route, prev) }) }, err => { if (isNavigationFailure(err, NavigationFailureType.duplicated)) { this.index = targetIndex } } ) }
成功
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
go
的方法相比于push
、replace
方法,找到对应路由后,没有使用transitionTo
包裹,直接使用confirmTransition
跳转,这是因为go
是对历史记录进行操作,已经有对应的route
对象,不需要重新匹配,只需要按照对应的route
对象进行切换组件即可
# 总结
abstract
模式被设计为在没有浏览器API支持的环境下使用(例如服务器端渲染、React Native等)。在这种模式下,路由的改变并不会像在浏览器环境中那样体现在URL的变化中。所以在该模式的源码中,没有这部分的操作。
abstract
模式通过对路由历史记录栈和索引的管理,实现路由的切换
push
方法,直接删除索引后续的历史记录,将新的路由添加到历史记录栈的顶端replace
方法,用新的记录替换当前记录,并舍去后续历史记录go
方法,找到历史记录中的route
对象后,执行组件切换