手写简易版 VueRouter
简易版 VueRouter,直接引入也可以用
js
let Vue; // 插件中的全局 Vue
class VueRouter {
constructor(options) {
this.$options = options;
this.current = window.location.hash.slice(1) || '/';
// 利用 vue 工具函数把 matched 设置为响应式数据
Vue.util.defineReactive(this, 'matched', []);
this.match(); //递归出 routes 表
window.addEventListener('hashchange', this.onHashchange.bind(this));
window.addEventListener('load', this.onHashchange.bind(this));
}
onHashchange() {
this.current = window.location.hash.slice(1);
this.matched = [];
this.match();
}
match(routes) {
routes = routes || this.$options.routes;
// 递归遍历
for (const route of routes) {
// / 路由
if (route.path === '/' && this.current === '/') {
this.matched.push(route);
return;
}
// /helle/child 子路由
if (route.path !== '/' && this.current.indexOf(route.path) !== -1) {
this.matched.push(route);
if (route.children) {
this.match(route.children); // 递归子路由
}
return;
}
}
}
}
// _Vue 在 vue.use 时传入
VueRouter.install = function (_Vue) {
Vue = _Vue; //保存参入的 Vue,不用引入也能使用
// 全局混入,挂载 $router,延迟下面逻辑到 router 被添加到 new vue 选项时才执行
Vue.mixin({
beforeCreate() {
if (this.$options.router) {
Vue.prototype.$router = this.$options.router;
}
},
});
// router-link,router-view组件
Vue.component('router-link', {
// template: '<a>aaa</a>'
props: {
to: {
type: String,
require: true,
},
},
render(h) {
return h('a', { attrs: { href: '#' + this.to } }, this.$slots.default);
},
});
Vue.component('router-view', {
// template: '<a>bbb</a>'
render(h) {
// 标记当前 router-view 深度
this.$vnode.data.routerView = true;
let depth = 0; // 深度
let parent = this.$parent; // vue 中 vnode 里有一个 $parent 属性
while (parent) {
const vnodeData = parent.$vnode && parent.$vnode.data;
if (vnodeData && vnodeData.routerView) {
// 当前 parent 是一个 router-view
depth++;
}
parent = parent.$parent;
}
// 通过 depth 获取当前路由对应的组件
let component = null;
const route = this.$router.matched[depth];
if (route) {
component = route.component;
}
return h(component);
},
});
};
export default VueRouter;