Vue-Router模拟实现-hash模式

651 阅读1分钟

「这是我参与2022首次更文挑战的第12天,活动详情查看:2022首次更文挑战

模拟 VueRouter 的 hash 模式的实现

模拟 VueRouter 的 hash 模式的实现,实现思路和 History 模式类似,把 URL 中的 # 后面的内容作为路由的地址,可以通过 hashchange 事件监听路由地址的变化。

vue-router是什么?

官方定义:是 Vue.js 官方的路由管理器。它和 Vue.js 的核心深度集成,让构建单页面应用变得易如反掌。

Hash模式

  • URL中#后面的内容作为路径地址
  • 监听hashchange事件
  • 根据当前路由地址找到对应组件重新渲染

VueRouter一个对象实例,所以VueRouter为一个构造函数或者一个类,我们以类的方式来实现,VueRouter这个类里面有

个静态的install方法,里面传入了一个对象的形式,里面传入了路由的路径及对应的组件,然后在main.js中创建Vue实例,

传入router对象。

类图构造

image.png

完整代码

let _vue = null;

export default class Router {
  $options = null;
  routeMap = null;
  data = null;
  // Vue.use 方法会调用对应组件的 install 方法
  static install(Vue) {
    // 1. 判断插件是否被安装
    if (Router.install.installed) {
      return;
    }
    Router.install.installed = true;
    // 2. 将`install`方法中的参数`vue`插入到全局中
    _vue = Vue;
    // 3. 将创建`vue`实例时候传入的`Router`对象注入到`vue`实例上
    // 使用混入
    _vue.mixin({
      beforeCreate() {
        if (this.$options.router) {
          _vue.prototype.$router = this.$options.router;
          this.$options.router.init();
        }
      },
    });
  }

  constructor(options) {
    // 记录构造函数中传入的选项
    this.options = options;
    // 将来将options的规则解析到 routeMap中来,键值对的形式
    this.routeMap = {};
    // data是响应式的对象,使用vue的提供的方法,方便之后路由更新我们可以及时更新页面
    this.data = _vue.observable({
      current: "/",
    });
  }
  /**
   * @description 注册初始化方法
   */
  init() {
    this.createRouteMap();
    this.initComponents(_vue);
    this.initEvent();
  }
  /**
   * @description  遍历路由规则,把路由规则解析成键值对的形式,储存到routeMap中
   */
  createRouteMap() {
    this.options.routes.forEach((route) => {
      this.routeMap[route.path] = route.component;
    });
  }
  /**
   *
   * @param {*} vue 的实例
   */
  initComponents(vue) {
    const self = this;
    vue.component("router-link", {
      props: {
        to: String,
      },
      // template: '<a :href="to"><slot></slot></a>',
      render(h) {
        return h(
          "a",
          {
            attrs: {
              href: "#" + this.to,
            },
            on: {
              click: this.clickHander,
            },
          },
          [this.$slots.default]
        );
      },
      methods: {
        clickHander(e) {
          history.pushState({}, "#", this.to);
          this.$router.data.current = "#" + this.to;
          e.preventDefault();
        },
      },
    });
    vue.component("router-view", {
      render(h) {
        // 我们需要先找到当前的路由地址 self.data.current
        // 找到对应路由所对应的组件
        const component = self.routeMap[self.data.current];
        // h函数可以将我们的路由转换成虚拟dom传递给页面
        return h(component);
      },
    });
  }
  initEvent() {
    window.addEventListener("hashchange", this.onHashChange.bind(this));
    window.addEventListener("load", this.onHashChange.bind(this));
  }
  onHashChange() {
    //当路由地址发生变化,重新记录当前的路径
    this.data.current = window.location.hash.substr(1) || "/";
  }
}