手写简版vue-router(hash模式版) | 8月更文挑战

416 阅读2分钟

这是我参与8月更文挑战的第1天,活动详情查看:8月更文挑战

单页面应用程序中,url发生变化的时候,不能刷新页面但是对于的视图内容会发生变化

1. 要手写咱们先来说说vue-router的使用步骤

  1. 在router.js文件里面使用vue-router插件
     import Router from 'vue-router
     Vue.use(Router)
    
  2. 在router.js文件里面创建Router实例并导出
    export default new Router({...}) 
    
  3. 在main.js文件里面在根实例上添加该实例
    import router from './router
    new Vue({
        router,
    }).$mount("#app");
    
  4. 在App.vue文件里面添加路由视图以及路由跳转链接
    <router-link to="/home"></router-link>
    <!--路由出口-->
    <router-view></router-view>
    
    this.$router.push('/')
    this.$router.push('/home')
    

2. 通过使用步骤咱们来思考几个问题,带着这些疑问咱们去做,疑问解决了也就懂了

  1. Vue.use(Router) 这个use干什么用的为啥要use一下(是否Vue插件都要这么处理呢?)
  2. new Vue({ router, }).$mount("#app");这里为什么要将router实例放到new Vue的options里面
  3. <router-link to="/home"></router-link><router-view></router-view>这二个组件为什么可以直接使用
  4. this.router.push为什么在Vue实例里面可以使用router.push为什么在Vue实例里面可以使用router这个路由器实例

3. 我们的需求

  1. SPA页面不能刷新页面
    1. hash对应来切换 ==> hashchange事件
    2. h5的History api来实现
  2. 根据url来显示的内容要做的事
    1. 实现组件router-view与router-link
    2. 数据响应式处理,我的hash变了,就应该动态的重新渲染页面的内容 render

4. 根据需求以及疑问我们要完成的事情也就有了

  1. 实现一个插件为主线
    1. 实现VueRouter类
      • 处理路由器选项(options)
      • 监控url变化,hashChange
      • 响应这个变化,渲染新的内容
    2. 实现install方法(所有的vue插件都必须要有install方法),调用时机就是在Vue.use的时候会触发
      • $router注册到Vue实例上去
      • 实现router-view 与 router-link全局组件

5. 代码实现

let Vue;
class VueRouter {
    constructor(options){
        this.$options = options;
        this.$routerMap = {};
        // current的做成响应式的
        // 实现方法呢? ==> 
        // 1.用new Vue()来实现
        // 2.用Vue.util.defineReactive()来实现
        // this.current = window.location.hash.slice(1) || '/'; 

        Vue.util.defineReactive(this, 'current' ,window.location.hash.slice(1) || '/')
        window.addEventListener('hashchange', () => {
            // 这里虽然改变了current但是这个current并不是响应式的,
            // 所以不会触发组件更新(如果不用Vue.util.defineReactive处理的话)
            this.current =  window.location.hash.slice(1)
        })

        // 存储一份路由表对象后面就不需要去遍历了
         this.$options.routes.forEach(item =>{
            this.$routerMap[item.path] = item
        })
        
    }

}
VueRouter.install = function(_vue){
    // 所以vue-router对Vue是强引用
    Vue = _vue;
    // $router注册到Vue实例里面
    // 但是我们此时不能获得到Vue实例那我们需要在某个时机去做这个注册的事 ===> 混入Vue.mixin
   Vue.mixin({
        beforeCreate() {
            // 这里拿到的this就是vue实例
            console.log(this)
            if(this.$options.router){ 
                //根组件 实例 才会有router在实例化的时候传入的
                Vue.prototype.$router = this.$options.router;
                console.log(this.$options.router)
            }
        }
    })
    // // 实现二个全局组件 router-link 与 router-view
    Vue.component('router-link',{
        // 用法<router-link to="/home">主页</router-link>所以href就可以从props属性来获取
        props: {
            to: {
               type: String,
               require: true 
            }
        },
        render(h){
            // this.$slots.default就是下载router-link标签里面的内容例如:主页
           return h('a', {attrs: { href: '#' + this.to }}, this.$slots.default)
        }
    }) 

    Vue.component('router-view', { 
        // render的执行时机
        // 1.init初始化的时候
        // 2.依赖的值发生变化的时候会执行
         
        render(h){
        // 这个组件就是根据url来渲染对应的组件内容配置项
        // {
        //     path: '/',
        //     name: 'app',
        //     component: Home
        // }
        // 1. 获取hash部分 #/home 如何获取呢 通过window.location.hash
            // 我们的current现在是响应式了
            // 所以this.$router.current这个是动态变化的就会触发render的更新
        // 2. 根据上面的地址获取对应组件配置home组件,那么我们如何获取到路由的配置选项呢?
            // this.$router.$routerMap就是路由映射表
        // 3. h(Home)
            console.log(this.$router.$options)
            console.log(this.$router.current)
            console.log(this.$router.$routerMap);
            let Component;
            Component = this.$router.$routerMap[this.$router.current].component
            return h(Component)
        }})

}
export default VueRouter

6. 这是最最简单一种vue-router的实现(利用hash来做的)

7. 还有一些问题以及思考

1. 嵌套路由不支持要怎么处理也就是配置项里面有children选项的时候代码怎么写
2. 在创建路由配置项的时候是可以传递一个属性:mode:'history', history模式的代码怎么实现
3. 为什么Vue要在install的时候传入而不是在里面通过import引入呢(减少依赖不用打包)

总结:每天进步一点点 总会从超鬼变成超神的