Skip to content

手写简易版 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;

基于 MIT 许可发布