在new vueRouter的时候我们可以传入一个mode属性,他可以接收三个值:hash/history/abstract
hash和history的区别
history的路径更美观一点 比如http://yoursite.com/user/id,history是基于pushState()来完成 URL 跳转而无须重新加载页面。 但是强制刷新还是会有问题(服务端来解决这个问题),所以history模式需要后端人员配合使用。
hash的路径会带有#,比如http://yoursite.com#/user/id
HashHistory
1
2
3
4
5
6
7
|
class VueRouter{ constructor(options){ this .matcher = createMatcher(options.routes || []); //这里为了讲解hash模式 所以就不进行判断用户传进来的是哪种模式了 this .history = new HashHistory( this ); //this vue-router的实例 } } |
源码这里创建了一个基类我们这里和源码统一,这个基类封装了三种模式公用的方法和属性,那么我们在这里创建一个HashHistory和基类History
1
2
3
4
5
6
7
8
9
10
11
12
13
|
import History from './base' // hash路由 export default class HashHistory extends History{ constructor(router){ super (router); //继承调用父类 等于call } } // 路由的基类 export default class History { constructor(router){ this .router = router; } } |
如果是hash路由,打开网站如果没有hash默认应该添加#/
1
2
3
4
5
6
7
8
9
10
11
12
13
|
import History from './base' ; function ensureSlash(){ if (window.location.hash){ return } window.location.hash = '/' } export default class HashHistory extends History{ constructor(router){ super (router); ensureSlash(); // 确保有hash } } |
再看一下初始化的逻辑(上面的router.init函数)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
init(app){ const history = this .history; // 初始化时,应该先拿到当前路径,进行匹配逻辑 // 让路由系统过度到某个路径 const setupHashListener = ()=> { history.setupListener(); // 监听路径变化 } history.transitionTo( // 父类提供方法负责跳转 history.getCurrentLocation(), // 子类获取对应的路径 // 跳转成功后注册路径监听,为视图更新做准备 setupHashListener ) } |
这里我们要分别实现 transitionTo(基类方法)、 getCurrentLocation 、setupListener
getCurrentLocation实现
1
2
3
4
5
6
7
8
9
|
function getHash(){ return window.location.hash.slice(1); } export default class HashHistory extends History{ // ... getCurrentLocation(){ return getHash(); } } |
setupListener实现
1
2
3
4
5
6
7
8
9
|
export default class HashHistory extends History{ // ... setupListener(){ window.addEventListener( 'hashchange' , ()=> { // 根据当前hash值 过度到对应路径 this .transitionTo(getHash()); }) } } |
TransitionTo实现
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
|
export function createRoute(record, location) { // {path:'/',matched:[record,record]} let res = []; if (record) { // 如果有记录 while (record){ res.unshift(record); // 就将当前记录的父亲放到前面 record = record.parent } } return { ...location, matched: res } } export default class History { constructor(router) { this .router = router; // 根据记录和路径返回对象,稍后会用于router-view的匹配 this .current = createRoute( null , { path: '/' }) } // 核心逻辑 transitionTo(location, onComplete) { // 去匹配路径 let route = this .router.match(location); // 相同路径不必过渡 if ( location === route.path && route.matched.length === this .current.matched.length){ return } //更新路由并且下面会提到改变根实例上的_route属性 this .updateRoute(route) onComplete && onComplete(); } } |
1
2
3
4
5
6
7
|
export default class VueRouter{ // ... //做一个代理 match(location){ return this .matcher.match(location); } } |
macth方法
1
2
3
4
5
6
7
8
9
10
11
12
13
|
function match(location){ // 稍后根据路径找到对应的记录 let record = pathMap[location] if (record) { // 根据记录创建对应的路由 //参数:/about/a:{path:xx,component...},path:'/about/a' return createRoute(record,{ path:location }) } // 找不到则返回空匹配 return createRoute( null , { path: location }) } |
我们不难发现路径变化时都会更改current属性,我们可以把current属性变成响应式的,每次current变化刷新视图即可
在install方法中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
install(Vue) { Vue.mixin({ // 给所有组件的生命周期都增加beforeCreate方法 beforeCreate() { if ( this .$options.router) { //调用Vue类中双向数据绑定方法 Vue.util.defineReactive( this , '_route' , this ._router.history.current); } } }); // $route和$router方法 这两个方法仅仅是vue中最常见的代理 仅仅是为了更加方便 Object.defineProperty(Vue.prototype, '$route' ,{ // 每个实例都可以获取到$route属性 get(){ return this ._routerRoot._route; //上面刚进行双向数据绑定的 } }); Object.defineProperty(Vue.prototype, '$router' ,{ // 每个实例都可以获取router实例 get(){ return this ._routerRoot._router; } }) } |
切换路由每次初始化时都需要调用更新_route的方法,因为install的时候把_route进行双向数据绑定,刚进来是没有this._router.history.current的,通过发布订阅方式来进行订阅和更新操作;在init方法中增加监听函数
1
2
3
|
history.listen((route) => { // 需要更新_route属性,出入一个函数 app._route = route }); |
1
2
3
4
5
6
7
8
9
10
11
12
13
|
export default class History { constructor(router) { // ... this .cb = null ; } listen(cb){ this .cb = cb; // 注册函数 } updateRoute(route){ this .current =route; this .cb && this .cb(route); // 更新current后 更新_route属性 } } |
以上就是详解vue的hash跳转原理的详细内容,更多关于vue的hash跳转原理的资料请关注服务器之家其它相关文章!
原文链接:https://segmentfault.com/a/1190000039369103