手写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;

  

原文地址:https://www.cnblogs.com/gengzhen/p/15404937.html