手写vue-router简版
router一共两个文件,一个是new Vue({(router/index)})
index.js
import Vue from "vue";
import VueRouter from "./zhenVue-router";
import Home from "../views/Home.vue";
// 1.VueRouter是一个插件?
// 内部做了什么:
// 1)实现并声明两个组件router-view router-link
// 2) install: this.$router.push()
Vue.use(VueRouter);
const routes = [
{
path: "/",
name: "Home",
component: Home,
},
{
path: "/about",
name: "About",
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () =>
import(/* webpackChunkName: "about" */ "../views/About.vue"),
children: [
{
path: "/about/info",
component: {
render(h) {
return h("div", "info page");
},
},
},
],
},
];
// 2.创建实例
const router = new VueRouter({
mode: "hash",
base: process.env.BASE_URL,
routes,
});
export default router;
第二个文件是vue.use(install(Vue)) zhenVue-router.js
// 1.install插件
// 2.两个组件 router-link,router-view
let Vue; //保存Vue构造函数,插件中要使用
class VueRouter {
constructor(options) {
this.$options = options;
// 将current作为响应式数据,将来发生变化,router-view的render函数能够再次执行
const initial = window.location.hash.slice(1) || "/";
Vue.util.defineReactive(this, "current", initial);
// this.current = "/";
window.addEventListener("hashchange", () => {
this.current = window.location.hash.slice(1); //把#截取掉
});
}
static install(_Vue) {
Vue = _Vue;
// 挂载$router属性; this.$royter.push()
// Vue.use(install)的执行时间在很靠前的,这个时候还获取不到VueRouter实例中,所以要巧妙的运用到Vue.mixin的beforeCreate()方法获取并挂载router
// 全局混入(每个组件都会用到)
// 全局混入目的:延迟下面逻辑到router已经创建完毕并且附加到选项上时才执行
Vue.mixin({
beforeCreate() {
// 此钩子在每个组件创建实例时都会调用
// 所有组件都有$option,但只有根实例才有this.$option.router.根实例是new Vue()
//this.$option.router即VueRouter的实例,也是new Vue()的options
if (this.$options.router) {
Vue.prototype.$router = this.$options.router;
}
},
});
// 注册并实现两个组件router-view,router-link
Vue.component("router-link", {
props: {
to: {
type: String,
required: true,
},
},
render(h) {
// <a href="to">xxxx</a>
return h("a", { attrs: { href: "#" + this.to } }, this.$slots.default);
},
});
Vue.component("router-view", {
// 获取当前路由对应的组件
render(h) {
// console.log(this); //this指向的是router-view的组件实例
// console.log(this.$router); //this.$router,每一个组件都有this.$router,因为是在Vue.prototype上面挂载的,this.$router其实就是router实例
// console.log(this.$router.$options);
let component = null;
const route = this.$router.$options.routes.find((route) => {
return route.path === this.$router.current;
});
console.log(route);
if (route) {
component = route.component;
}
return h(component);
},
});
}
}
export default VueRouter;
上面只考虑到了单层路由,接下来是嵌套路由的解决方式
1、router-view深度标记depth
2、路由匹配时获取代表深度层级的matched数组
// 1.install插件
// 2.两个组件 router-link,router-view
let Vue; //保存Vue构造函数,插件中要使用
class VueRouter {
constructor(options) {
this.$options = options;
// 将current作为响应式数据,将来发生变化,router-view的render函数能够再次执行
// const initial = window.location.hash.slice(1) || "/";
// Vue.util.defineReactive(this, "current", initial);
this.current = window.location.hash.slice(1) || "/";
// 路由匹配时获取代表深度层级的matched数组
Vue.util.defineReactive(this, "matched", []);
// match方法可以递归遍历路由表,获得匹配关系数组
this.match();
// this.current = "/";
// hashchange事件监听url变化
window.addEventListener("hashchange", () => {
this.current = window.location.hash.slice(1); //把#截取掉
// hashchange要清空matched数组,重新执行this.match给matched添加path对应的所有的组件,与depth对应上
this.matched = [];
this.match();
});
// 创建路由映射表 key为path.值为route本身
// this.routeMap = {};
// options.routes.forEach((route) => {
// this.routeMap[route.path] = route;
// });
}
match(routes) {
routes = routes || this.$options.routes;
//递归遍历路由表
for (const route of routes) {
if (route.path === "/" && this.current === "/") {
this.matched.push(route);
return;
}
// /about/info 例如 this.current是/about/info route.path ="/about"能够被匹配到
if (route.path !== "/" && this.current.indexOf(route.path) !== -1) {
//第一次匹配到/about
this.matched.push(route);
console.log(this.matched);
// /about的组件有children,递归route.children,这样matched数组就有/about 和 /about/info
if (route.children) {
this.match(route.children);
}
return;
}
}
}
static install(_Vue) {
Vue = _Vue;
// 挂载$router属性; this.$royter.push()
// Vue.use(install)的执行时间在很靠前的,这个时候还获取不到VueRouter实例中,所以要巧妙的运用到Vue.mixin的beforeCreate()方法获取并挂载router
// 全局混入(每个组件都会用到)
// 全局混入目的:延迟下面逻辑到router已经创建完毕并且附加到选项上时才执行
Vue.mixin({
beforeCreate() {
// 此钩子在每个组件创建实例时都会调用
// 所有组件都有$option,但只有根实例才有this.$option.router.根实例是new Vue()
//this.$option.router即VueRouter的实例,也是new Vue()的options
if (this.$options.router) {
Vue.prototype.$router = this.$options.router;
}
},
});
// 注册并实现两个组件router-view,router-link
Vue.component("router-link", {
props: {
to: {
type: String,
required: true,
},
},
render(h) {
// <a href="to">xxxx</a>
return h("a", { attrs: { href: "#" + this.to } }, this.$slots.default);
},
});
Vue.component("router-view", {
// 获取当前路由对应的组件
render(h) {
// console.log(this); //this指向的是router-view的组件实例
// console.log(this.$router); //this.$router,每一个组件都有this.$router,因为是在Vue.prototype上面挂载的,this.$router其实就是router实例
// console.log(this.$router.$options);
// let component = null;
// const route = this.$router.$options.routes.find((route) => {
// return route.path === this.$router.current;
// });
// console.log(route);
// if (route) {
// component = route.component;
// }
// const { routeMap, current } = this.$router;
// let component = routeMap[current].component || null;
// 标记当前router-view深度
this.$vnode.data.routerView = true; //在当前组件的节点上加一个routerView:true的标记
let depth = 0; //初始化depth深度为0
let parent = this.$parent; //找到父亲节点
console.log(parent);
// 一直往上查找parent,在parent看有没有routerView这个属性有的话depth++
while (parent) {
const vnodeData = parent.$vnode && parent.$vnode.data;
if (vnodeData) {
if (vnodeData.routerView) {
// 说明当前的parent是routerView
depth++;
}
}
parent = parent.$parent;
}
// 获取path对应的component
let component = null;
const route = this.$router.matched[depth];
if (route) {
component = route.component;
}
return h(component);
},
});
}
}
export default VueRouter;

浙公网安备 33010602011771号