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;