1.构造VueRouter类
constructor(options) {
/* 初始化 */
this.mode = options.mode || 'hash';
this.routes = options.routes || [];
/* 转化为{'/home':Home}的形式 */
this.routesMap = this.createMap(this.routes);
/* 初始化状态 */
this.history = new HistoryRoute();
/* 初始化路由设置 */
this.init();
}
2.将routes格式[{path:'/home',component:Home}]
转化为{'/home':Home}
的形式
createMap(routes) {
return routes.reduce((data, item) => {
data[item.path] = item.component;
return data;
}, {});
}
3.对hash模式和history模式的路径进行赋值,设置监听函数,用this.history.current
保存当前path
init() {
if (this.mode === 'hash') {
location.hash ? '' : location.hash = '/';
// 加载完成事件监听
window.addEventListener('load', () => {
this.history.current = location.hash.slice(1);
})
/* 监听hash改变 */
window.addEventListener('hashchange', () => {
this.history.current = location.hash.slice(1);
})
} else {
location.pathname ? '' : location.pathname = '/';
window.addEventListener('load', () => {
this.history.current = location.pathname;
})
/* 前进后退 */
window.addEventListener('popstate', () => {
this.history.current = location.pathname;
})
}
}
4.实现每个子组件this.$router
和this.$route
的功能
(1)mixin的内容所有组件适用,并且不会相互干扰
(2)找到根组件并设置this._root=this
,每次实例化子组件时继承父组件的_root
属性,然后通过数据劫持的方式获取到VueRouter实例
(3)对this._router.history
设置响应式,路径改变视图更新
VueRouter.install = (Vue, opts) => {
Vue.mixin({
/* 创建实例之前 */
beforeCreate() {
/* 只有根组件存在router */
if (this.$options && this.$options.router) {
this._root = this;/* 根组件 */
this._router = this.$options.router;/* 路由实例 */
/* 监听current */
Vue.util.defineReactive(this, 'xxx', this._router.history);
} else {
this._root = this.$parent._root;/* 传递根组件使每个子组件都可以访问到根组件 */
}
/* 劫持$router的获取 */
Object.defineProperty(this, '$router', {
get() {/* 获取根组件,然后取根组件的路由项 */
return this._root._router;
}
})
/* 劫持$route的获取 */
Object.defineProperty(this, '$route', {
get() {//路由状态
return this._root._router.history;
}
})
}
});
//省略...
}
5.创建router-link
与router-view
(1)router-link
负责提供触发入口,改变路由状态
(2)router-view
接受响应并放到render函数中进行视图更新
VueRouter.install = (Vue, opts) => {
//省略...
Vue.component('router-link', {
props: { to: String, tag: String },
methods: {
handleClick() {//点击router-link
let mode = this._self._root._router.mode;//模式
if (mode === 'hash') {
location.hash = this.to;
} else {
history.pushState({}, null, this.to);
}
//改变状态,从而更新router-view更新视图
this._self._root._router.history.current = this.to;
}
},
render(h) {
let mode = this._self._root._router.mode;
let tag = this.tag ? this.tag : 'a';
//区别a标签和自定义标签
if (tag === 'a') return <a href={mode === 'hash' ? `#${this.to}` : this.to}>{this.$slots.default}</a>;
else return <tag onclick={this.handleClick}>{this.$slots.default}</tag>
}
});
Vue.component('router-view', {
render(h) {
/* current加载前获取,需要在beforCreated中设置成响应式对象*/
let current = this._self._root._router.history.current;/* /home */
let routesMap = this._self._root._router.routesMap;
return h(routesMap[current]);//渲染得到的组件
}
})
}