vue.js-手写vue-router

265 阅读1分钟

Vue Router实现原理

1) 基本原理

  • hash
    • URL 中 # 后面的内容作为路径地址
    • 监听 hashchange 事件
    • 根据当前路由地址找到对应组件重新渲染
  • history
    • 通过 history.pushState() 方法改变地址栏
    • 监听 popState 事件
    • 根据当前路由地址找到对应组件重新渲染

2)模拟实现

1)Vue 的构建版本

  • 运行时版:不支持 template 模板,需要打包的时候提前编译(vue-cli 创建的项目默认是 运行时版)
  • 完整版:包含运行时和编译器,体积比运行时版大 10k 左右,程序运行的时候把模板转换成 render 函数
import Vue from 'vue'
console.dir(Vue)
let _Vue = null
export default class VueRouter {
  // 实现 vue 的插件机制
  static install(Vue) {
    //1 判断当前插件是否被安装
    if (VueRouter.install.installed) {
      return;
    }
    VueRouter.install.installed = true
    //2 把Vue的构造函数记录在全局
    _Vue = Vue
    //3 把创建Vue的实例传入的router对象注入到Vue实例
    // _Vue.prototype.$router = this.$options.router
    // 混入
    _Vue.mixin({
      beforeCreate() {
        if (this.$options.router) {
          _Vue.prototype.$router = this.$options.router

        }
      }
    })
  }

  // 初始化属性
  constructor(options) {
    this.options = options // options 记录构造函数传入的对象
    this.routeMap = {} // routeMap 路由地址和组件的对应关系
    // observable     data 是一个响应式对象
    this.data = _Vue.observable({
      current: "/" // 当前路由地址
    })
    this.init()

  }

  // 调用 createRouteMap, initComponent, initEvent 三个方法
  init() {
    this.createRouteMap()
    this.initComponent(_Vue)
    this.initEvent()
  }

  // 用来初始化 routeMap 属性,路由规则转换为键值对
  createRouteMap() {
    //遍历所有的路由规则 把路由规则解析成键值对的形式存储到routeMap中
    this.options.routes.forEach(route => {
      this.routeMap[route.path] = route.component
    });
  }

  // 用来创建 router-link 和 router-view 组件
  initComponent(Vue) {
    // router-link 组件
    Vue.component('router-link', {
      props: {
        to: String
      },
      // 模板 --- 需要 vue 完整版
      // 在 vue.config.js 中设置 runtimeCompiler: true,  
      // template: '<a :href="to"><slot></slot></a>'

      // render --- 可在 vue 运行时版直接使用
      render(h) {
        // h(选择器(标签的名字), 属性,生成的某个标签的内容)
        return h('a', {
          attrs: {
            href: this.to,
          },
          // 注册事件
          on: {
            click: this.clickHandler // 点击事件
          },
        }, [this.$slots.default]) // this.$slot.default 默认插槽
      },
      methods: {
        clickHandler(e) {
          // history.pushState(事件对象,网页标题,url地址)
          history.pushState({}, '', this.to)
          this.$router.data.current = this.to
          e.preventDefault() // preventDefault 阻止事件的默认行为
        }
      }
    });
    // router-view 组件
    const self = this; //这里的 this 指向 vueRouter 实例
    Vue.component('router-view', {
      render(h) {
        // 根据 routerMap 中的对应关系,拿到当前路由地址所对应的组件
        const component = self.routeMap[self.data.current]
        return h(component)
      }
    })
  }

  // 用来注册 popstate 事件
  initEvent () {
      window.addEventListener('popstate', () => {
          // 当前 url 地址的 pathname 部分赋值给 data.current
          this.data.current = window.location.pathname
      })
  }
}