实现Vue router

186 阅读2分钟

创建一个router类

class VueRouter {
    constructor(){

    }
}

router作为插件,在使用的时候调用的是Vue.use(),也就是会调用插件的install方法,所以实现一下VueRouter的install方法

VueRouter.install = function (Vue) {
    // 存储一下当前的Vue 减少当前引入Vue造成这里的Vue被打包
    _Vue = Vue
    // 挂载$router在全局的vue实例上
    Vue.mixin({
        beforeCreate(){
            if (this.$options.router) {
                Vue.prototype.$router = this.$options.router
            }
        }
    })
}

这里的mixin挂在全局Vue上,会在每个vue实例的beforeCreate钩子都执行这个代码,除非当前vue实例有自己定义的beforeCreate钩子函数 所以这里混入的顺序是 执行main里面的

import router from './router'

之后进入router文件夹下的index.js执行

Vue.use(Router)

也就是会执行VueRouter的install方法,这个时候就开始定义好了beforeCreate的默认方法

export default new Router({
  routes: [
    {
      path: '/',
      name: 'HelloWorld',
      component: HelloWorld
    },
    {
      path: '/home',
      name: 'home',
      component: ()=>import('../components/home')
    }
  ]
})

抛出路由实例,并且回到main.js执行new Vue

new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>'
})

这个时候规格的路由实例作为参数传给我们整个项目的根实例 进入到beforeCreate拿到参数判断到了是根实例将$router挂载在vue实例的原型上

这个时候再回到构造函数上,我们这个时候构造的实例已经被挂在vue实例上也就是每个vue实例都能拿到了,我们需要存储一个当前的hash值,并且当这个哈希值发生变化的时候触发路由容器的刷新

class VueRouter {
    constructor(options){
        // 保存一下路由配置
        this.$options = options
        // 保存当前路由
        let defaultHash = window.location.hash.slice(1) || '/'
        // 改变当前路由变量为一个响应式变量,这样调用到这个变量的vue实例会执行render函数重新渲染
        _Vue.util.defineReactive(this,'currenRoute',defaultHash)
        window.addEventListener('hashchange',this.hashChange.bind(this))
        window.addEventListener('load',this.hashChange.bind(this))
    }
    hashChange(){
        this.currenRoute = window.location.hash.slice(1) || '/'
    }
}

这里注意一下render函数中的craterElement方法可以直接传一个组件进行渲染,也可以进行标签渲染,其中标签渲染的时候三个参数分别为:1、标签名 2、标签配置 3、childs数组

再在install方法里实现路由容器组件

// router-view
    Vue.component('router-view',{
        render(h){
            // 获取到路由映射表,拿到当前的路由哈希值对应的组件
            let component = null
            let route = this.$router.$options.routes.find(item => item.path===this.$router.currenRoute)
            if (route) {
                component = route.component
            }
            return h(component)
        }
    })

实现路由跳转标签

Vue.component('router-link',{
        props:{
            to:{
                type: String,
                required: true
            }
        },
        render(h){
            return h('a',{attrs:{href:'#'+this.to}},this.$slots.default)
        }
    })

实现全部代码

将路由文件改成我们自己的路由文件

import Vue from 'vue'
// 改成自己的文件
import Router from './vue-router'
import HelloWorld from '@/components/HelloWorld'

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/',
      name: 'HelloWorld',
      component: HelloWorld
    },
    {
      path: '/home',
      name: 'home',
      component: ()=>import('../components/home')
    }
  ]
})

实现路由文件


let _Vue
class VueRouter {
    constructor(options){
        // 保存一下路由配置
        this.$options = options
        // 保存当前路由
        let defaultHash = window.location.hash.slice(1) || '/'
        // 改变当前路由变量为一个响应式变量,这样调用到这个变量的vue实例会执行render函数重新渲染
        _Vue.util.defineReactive(this,'currenRoute',defaultHash)
        window.addEventListener('hashchange',this.hashChange.bind(this))
        window.addEventListener('load',this.hashChange.bind(this))
    }
    hashChange(){
        this.currenRoute = window.location.hash.slice(1) || '/'
    }
}
VueRouter.install = function (Vue) {
    // 存储一下当前的Vue 减少当前引入Vue造成这里的Vue被打包
    _Vue = Vue
    // 挂载$router在全局的vue实例上
    Vue.mixin({
        beforeCreate(){
            if (this.$options.router) {
                Vue.prototype.$router = this.$options.router
            }
        }
    })
    // 实现一下路由的两个全局组件
    // router-view
    Vue.component('router-view',{
        render(h){
            // 获取到路由映射表,拿到当前的路由哈希值对应的组件
            let component = null
            let route = this.$router.$options.routes.find(item => item.path===this.$router.currenRoute)
            if (route) {
                component = route.component
            }
            return h(component)
        }
    })
    // router-link
    Vue.component('router-link',{
        props:{
            to:{
                type: String,
                required: true
            }
        },
        render(h){
            return h('a',{attrs:{href:'#'+this.to}},this.$slots.default)
        }
    })

}
 export default VueRouter