简易版 vue-router的 hash 模式实现

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

  • 答:自己实现的 vue-router 的代码如下(完整代码见https://github.com/smallSix6/fed-e-task-liuhuijun/tree/master/fed-e-task-03-01/code/liuzi-hash-router):
    • router/index.js
      import Vue from "vue";
      import VueRouter from "../vueRouter";
      import Home from "../views/Home.vue";
      // 1、注册路有插件
      // Vue.use 是用来注册插件,他会调用传入对象的 install 方法,如果是函数就直接调用函数
      Vue.use(VueRouter);
      
      // 路由规则
      const routes = [
          // 嵌套路由
          {
              path: "/",
              name: "Home",
              component: Home
          },
          {
              path: "/about",
              name: "About",
              // 开启 props 会把 URL 中的参数传递给组件
              // 在组件中通过 props 来接收 URL 参数
              props: true,
              // 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")
          }
      ];
      
      // 创建 router 对象
      const router = new VueRouter({
          routes
      });
      
      export default router;
      
    • vueRouter/index.js
      let _Vue = null
      export default class VueRouter {
          static install(Vue) {
              // 1、判断当前插件是否已经安装
              if (VueRouter.install.installed) {
                  return
              }
              VueRouter.install.installed = true
                  // 2、把 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
              this.routeMap = {}
              this.data = _Vue.observable({
                  current: '/'
              })
          }
          init() {
              this.createRouteMap()
              this.initComponents(_Vue)
              this.initEvent()
          }
          createRouteMap() {
              // 遍历所有的路由规则,把路由规则解析成键值对的形式,存储到 routeMap 中
              this.options.routes.forEach(route => {
                  this.routeMap[route.path] = route.component
              })
          }
          initComponents(Vue) {
              const self = this
              Vue.component(
                  'router-link', {
                      props: {
                          to: String
                      },
                      render(h) {
                          return h('a', {
                              attrs: {
                                  href: '#' + this.to
                              },
                              on: {
                                  click: this.clickHandler
                              }
                          }, [this.$slots.default])
                      },
                      methods: {
                          clickHandler(e) {
                              window.location.hash = '#' + this.to
                              this.$router.data.current = this.to
                              e.preventDefault()
                          }
                      }
                      // template: ''
                  }
              )
      
              Vue.component('router-view', {
                  render(h) {
                      const conmponent = self.routeMap[self.data.current]
                      return h(conmponent)
                  }
              })
          }
      
          initEvent() {
              window.addEventListener('load', this.hashChange.bind(this))
              window.addEventListener('hashchange', this.hashChange.bind(this))
          }
          hashChange() {
              if (!window.location.hash) {
                  window.location.hash = '#/'
              }
              this.data.current = window.location.hash.substr(1)
      
          }
      }
      
  • 实现思路:
    • 在 router/index.js 里面引入 vueRouter/index.js 模块,并注册路由插件,然后创建 router 对象
    • 在 vueRouter/index.js 中创建 VueRouter 类,并创建静态方法 install,这个方法做了如下事情:
      • 判断当前插件是否已经安装
      • 把 vue 构造函数记录到全局变量
      • _Vue.mixin: 用混入的方式把创建 vue 实例时候传入的 router 对象注入到 vue 实例上,并调用了初始化 init 的方法
    • 在 init 方法里面我们做了三件事
      • createRouteMap: 遍历所有的路由规则,把路由规则解析成键值对的形式,存储到 routeMap 中
      • initComponents: 初始化和 router 相关的 组件
      • initEvent: 监听页面 load 和 hashchange 方法,在这个地方有个判断,如果当前页面的 hash 不存在,则自动加上 ‘#/’ ,并加载 ‘/’ 的组件
      • 在 constructor 中我们会接收传过来的参数 options 并保存在实例中。在这儿有个需要注意的点,我们使用了 Vue.observable 方法使 current 变为响应式对象,这样在 current 变化的时候会重新渲染依赖 current 变量的组件,如本例中依赖 current 变量的 router-view 组件。

你可能感兴趣的:(vue)