美文网首页
手写vue-router

手写vue-router

作者: meng_281e | 来源:发表于2020-01-17 17:14 被阅读0次
一、准备工作
//定义三个组件:
  const Home = { template: '<div>首页</div>' }  
  const pageOne = { template: '<div>pageOne</div>' }      
  const pageTwo = { template: '<div>pageTwo </div>' }
//定义一个路由数组
 const routes = [
    {
      path: '/',
      component: Home
    },
    {
      path: '/pageOne ',
      component: pageOne 
    },
    {
      path: '/pageTwo ',
      component: pageTwo ,
    }
  ]
//新建路由对象:
  const router = new Router({
    mode:'history',
    routes:routes
  });
 
   var vue = new Vue({
      el: '#app',
      router
   })

所以,上面的new Router()就是我们要实现的。

二、实现Router

根据上面的代码,可以看到Router是一个构造函数,接受一个参数。参数为一个对象,里面是我们定义的mode(路由模式),routers(路由数组)。

class  vueRouter {

  // 构造函数接受一个参数options,里面是我们定义的mode(路由模式),routers(路由数组)
  constructor(options){
      this.mode = options.mode || "hash";
      this.routes = options.routes || [];

      // 参数传进来的路由表数据格式不方便操作,封装一个createMap函数,来组装成
      // {
      //   '路径': 组件,
      //   '路径': 组件
      // } 
      // 键为路径,值为组件 的格式
      this.routesMap = this.createMap(this.routes);

      // 路由中需要存放当前的路径
      this.history ={
        current :null
      };

      // 开始初始化操作
      this.init();
  }
  init(){
      if(this.mode == 'hash'){

          // 先判断用户打开时有没有hash,没有就跳转到#/
          location.hash?' ':location.hash = '/';

          // 页面加载完成时,存放当前的路径
          window.addEventListener('load',()=>{
              this.history.current = location.hash.slice(1);
             //location.hash.slice(1) 路由#号之后的值
          });

          // hash路由改变时,存放当前的路径
          window.addEventListener('hashchange',()=>{
              this.history.current = location.hash.slice(1);
          })
      }else {//history模式同理 hash
          location.pathname?'':location.pathname = '/';
          window.addEventListener('load',()=>{
              this.history.current = location.pathname;
          });
          window.addEventListener('popstate',()=>{
              this.history.current = location.pathname;
          })
          
      }
  }
  createMap(routes){
      return routes.reduce((memo,current)=>{
          memo[current.path] = current.component
          return memo
      },{})
  }
}
实现vueRouter Install

使用vue.use就会调用install方法。
vue每个组件都有this.$router / this.$route 所以要mixin一下。(还不了解vue mixin的可以看一下我简书中《vue mixin》的讲解)

vueRouter.install = function(Vue,opts) {
    Vue.mixin({//混合方法
        beforeCreate(){ 
            if(this.$options && this.$options.router){ //定位跟组件

                //把当前实例挂载在_root上
                this._root = this;

                // 把router实例挂载在_router上
                this._router = this.$options.router 

                //history中的current变化也会触发
                Vue.util.defineReactive(this,'xxx',this._router.history);
            }else {
                // vue组件的渲染顺序  父 -> 子 -> 孙子
                this._root =  this.$parent._root;//获取唯一的路由实例
            }
            Object.defineProperty(this,'$router',{//Router的实例
                get(){
                    return this._root._router;
                }
            });
            Object.defineProperty(this,'$route',{
                get(){
                    return {
                        //当前路由所在的状态
                        current:this._root._router.history.current
                    }
                }
            })
        }
    });
    // 全局注册 router的两个组件
    Vue.component('router-link',{
        props:{
            to:String,
            tag:String
        },
        methods:{
            handleClick(){跳转方法
                
            }
        },
        render(h){
            let mode = this._self._root._router.mode;
            let tag = this.tag;
            return <tag on-click={this.handleClick} 
                      href={mode === 'hash'?`#${this.to}`:this.to}>{this.$slots.default}
                   </tag>
        }
    })
    Vue.component('router-view',{//根据当前的状态 current 对应相应的路由
        render(h){
            //将current变成动态的 current变化应该会影响视图刷新
            //vue实现双向绑定 重写Object.defineProperty
            let current = this._self._root._router.history.current;
            let routeMap = this._self._root._router.routesMap
            return h(routeMap[current])
        }
    })
}
export  default VueRouter;

相关文章

网友评论

      本文标题:手写vue-router

      本文链接:https://www.haomeiwen.com/subject/bqkczctx.html