手写一个简易版vue-router(hash路由)

207 阅读1分钟

vue-router是什么

vue-router是一个vue插件,插件介绍

vue-router用途

它是vue的路由管理器。可以做到当path改变,页面不刷新,render对应component。

注:路由是一种映射关系,path --> component。

如何使用vue-router

安装:vue add router

核心步骤:

  • 步骤一:使用vue-router插件,写在router.js里面
import Router from 'vue-router'
Vue.use(Router)
  • 步骤二:创建Router实例,写在router.js
export default new Router({...})
  • 步骤3:跟组件上挂载Router实例
import router from './router'
new Vue({
  router,
}).$mount('#app')
  • 步骤四: 添加路由出口
<router-view><router-view/>
  • 导航
<router-link to="xx"> xx <router-link />
this.$router.push(xx)

实现提问

  • Vue.use(Router) 使用了一个插件,在这里做了什么?
答:调用了插件内部的install方法
  • 这个install方法做了什么?
$router注册 (这里有个坑:有段逻辑需要延迟执行, Vue.use的时候,路由实例还未创建)
注册了两个全局组件(router-link,router-view)
  • 如何监听path变化,切换对应组件
hashChange事件,拿到当前path, path需要是响应式的(Vue.util.defineReactive)
拿到path后, 在路由表里面查找匹配项
根据匹配项渲染对应组件

任务拆解

  1. 实现一个VueRouter类
处理路由表
监听hashChange, 获取当前path
响应式数据path
  1. 实现install方法
注册$router
全局注册router-link,router-view

核心代码

// install时存Vue构造函数,VueRouter类中要用到
let Vue


class VueRouter {
  constructor(options) {
    // 保存路由表
    this.options = options
    
    // 定义响应式的属性current 
    const initial = window.location.hash.slice(1) || '/'    
    Vue.util.defineReactive(this, 'current', initial)
    
    // 监听hashchange事件
    window.addEventLister(
      'hashChange', 
      () => { this.current = window.location.hash.slice(1) || '/' }
    )
  }
}

VueRouter.install = function (_Vue) {
  Vue = _Vue
  
  // 注册$router, 注意这里需要延迟执行,Vue.use时这里还没有拿到路由表,所以用全局mixin
  // Vue.prototype.$router = ?
  Vue.mixin(
    {
      beforeCreate () {
        // 这里的this指的是组件实例
        if (this.$options.router) { Vue.prototype.$router = this.$options.router }
      }
    }
  )
  
  // 注册全局组件
  Vue.component('router-link', {
    props: {
      to: {
        type: String,
        required: true
      }
    },
    // template: '<div></div>'  // runtime版本不能用,因为没有compiler
    render (h) {
      return h('a', {
        attrs: {
          href: '#' + this.to
        }
      }, this.$slots.default)
    }
  })
  // 内容的载体
  Vue.component('router-view', {
    render (h) {
      // 1 根据hash 获取path
      // 2 在路由表里根据path 找到component
      // 3 调用render函数渲染组件
      // console.log(this.$router.$options, this.$router.current)
      let component = null
      const route = this.$router.$options.routes.find(item => item.path === this.$router.current)
      if (route) {
        component = route.component
      }
      return h(component)
    }
  })
}

手写代码地址:

简易版vue-router