简化版的vue-router实现思路详解

2020-06-14 06:22:42易采站长站整理

}

Object.defineProperty(this, '$router', {
get() {
return this._routerRoot._router;
}
})

Object.defineProperty(this, '$route', {
get() {
return {
current: this._routerRoot._routeHistory.current,
...this._routerRoot._router.route
};
}
})
}
});

Vue.component('router-view', {
render(h) { ... }
})

Vue.component('router-link', {
props: {
to: String,
tag: String,
},
render(h) { ... }
})
this._Vue = Vue;
}

这里的 this 代表的就是 vue-router 对象,它有两个属性暴露出来供外界调用,一个是 install ,一个是 Router 构造函数,这样可以保证插件的正确安装以及路由实例化。我们先忽略 Router 构造函数,来看 install ,上面代码中的 this._Vue 是个开始没有定义的属性,他的目的是防止多次安装。我们使用 Vue.mixin 对每个组件的 beforeCreate 钩子做全局混入,目的是让每个组件实例共享 router 实例,即通过 this.$router 拿到路由实例,通过 this.$route 拿到路由状态。需要重点关注的是这行代码:


Vue.util.defineReactive(this, '_routeHistory', this._router.history)

这行代码利用 vue 的响应式原理,对根 vue 实例注册了一个 _routeHistory 属性,指向路由实例的 history 对象,这样 history 也变成了响应式的。因此一旦路由的 history 发生变化,用到这个值的组件就会触发 render 函数重新渲染,这里的组件就是 router-view 。从这里可以窥察到 vue-router 实现的一个基本思路。上述的代码中对于两个全局组件的 render 函数的实现,因为会依赖于 router 对象,我们先放一放,稍后再来实现它们,下面我们分析一下 Router 构造函数。

Router构造函数

经过刚才的分析,我们知道 router 实例需要有一个 history 对象,需要一个保存当前路由状态的对象 route ,另外很显然还需要接受路由配置表 routes ,根据 routes 需要一个路由映射表 routerMap 来实现组件搜索,还需要一个变量 mode 判断是什么模式下的路由,需要实现 push 和 replace 两个api,代码如下:


const Router = function (options) {
this.routes = options.routes; // 存放路由配置
this.mode = options.mode || 'hash';
this.route = Object.create(null), // 生成路由状态
this.routerMap = createMap(this.routes) // 生成路由表
this.history = new RouterHistory(); // 实例化路由历史对象
this.init(); // 初始化
}
Router.prototype.push = (options) => { ... }
Router.prototype.replace = (options) => { ... }
Router.prototype.init = () => { ... }

我们看一下路由表 routerMap 的实现,由于不考虑嵌套等其他情况,实现很简单,如下: