zoukankan      html  css  js  c++  java
  • 手写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;
    

      

  • 相关阅读:
    [20180814]校内模拟赛
    [20180812]四校联考
    [20180811]校内模拟赛
    [20180613]校内模拟赛
    网络流24题小结
    最小费用最大流——小结1
    ASP.NET MVC 下拉框的传值的两种方式
    面向方面编程(AOP)
    NPOI操作Excel
    IIS负载均衡
  • 原文地址:https://www.cnblogs.com/gengzhen/p/15404937.html
Copyright © 2011-2022 走看看