美文网首页
简单实现vue-router

简单实现vue-router

作者: 叫维特的少年 | 来源:发表于2020-11-16 22:05 被阅读0次

    vue-router

    将路由引入到前端让构建单页应用变得简单,vue-router作为Vue的路由管理器,使我们在vue开发单页应用时更加易如反掌。
    接下来尝试手写一个简版的vue-router
    首先创建一个router类,并记录下调用router类时传入的值

    //用以存放vue构造函数,会用到vue构造函数
    let vm  = null;
    export default class vueRouter {
      constructor(options){
            //记录构造函数传入的选项
            this.options = options
            //拿到mode,默认hash模式
            this.mode = options.mode || "hash";
            //保存options里的路由规则
            this.routerMap = {}
            //记录当前路由地址,是响应的,所以使用vue.observable方法,默认是'/'
            this.data = vm.observable({
                current:'/'
            })
        }
    }
    

    作为vue的一个插件,在使用vue-router时需要调用Vue.use方法,此时会执行vueRouter中的install方法,所以创建一个router类,其中包含install的静态方法

        /*
        **router方法的一个静态方法,在vue中当使用vue.use方法来还在插件的时候都会调用对应插件内部的install方法进行安装
        **不管是router还是vuex在使用时都会调用各自的install方法进行加载
        */
        static install(Vue){
            /*  install要做的事情
                1.判断当前的插件是否一经被安装
                2.将vue构造函数记录到全局变量
                3.将router对象注入到vue实例中去        
            */ 
        //判断是否已存在
            if(VueRouter.install.installed){
                return;
            }
            VueRouter.install.installed = true;
            //设置全局变量
            vm = Vue
            //通过vue的mixin将router放到vue实例上
            vm.mixin({
                beforeCreate() {
                    // 判断是否是组件,组件的话不需要在进行挂在操作,此操作只进行一次就可
                    if(this.$options.router){
                        vm.prototype.$router = this.$options.router;
                        //进行初始化操作
                        this.$options.router.init();
                    }
                },
            })
        }
    

    之后需要遍历所有的路由规则,解析成键值对的方式存放在this.routeMap对象中去

        //遍历所有的路由规则并解析成键值对的形式存到this.routeMap对象中去
        creatRouterMap(){
            this.options.routes.forEach(route => {
                //取到routes每个元素的path和compnent放到routerMap中
                this.routerMap[route.path] = route.component
            })
        }
    

    对于router-link在页面最后的解析是生成a标签,且a标签的href属性内容就是router-link标签内to属性的内容,由此我们需要创建一个模板来解析router-link和其属性to,如果是history模式,那生成的a标签的herf属性就是to的内容,但如果是hash模式的话就会是会多一个#,并且url上也会有#,对于a标签的内容同样是router-link的内容
    例如:<router-link to="/">home</router-link>解析成 history模式下<a href='/'>home</a>,hash模式<a href='#/'>home</a>,其中home可以使用插槽slot来挖个坑,当解析router-link的时候会将内容填到slot插槽内。
    对于a标签需要添加一个点击事件,因为需要改变浏览器的url和相对应的视图,在hash模式下是改变location.hash的值,在history模式下这是使用pushstate来改变url地址,但不像hash一样,浏览器会记录pushState的历史记录,可以使用浏览器的前进、后退功能作用,同时还要阻止a标签的默认事件。

    initComponents (Vue){
       let _this = this;
       Vue.component('router-link',{
         props:{
             to:String
         },
         render(h){
             return h('a',{
                 attrs:{
                     //hash模式下a标签的href是#+path,history下就是path
                     //通过mode做个判断
                     href:_this.mode === 'hash' ? "#"+this.to : this.to
                 },
                 on:{
                     click:this.handler
                 }
             },[this.$slots.default])
         },
         methods:{
             handler(e){
                 switch (this.mode){
                     case 'hash':
                         window.location.hash = this.to;
                         break;
                     case 'history':
                         history.pushState({},'',this.to);
                         break;
                     default:
                         break;
                 }
                 this.$router.data.current = this.to;
                 e.preventDefault();
             }
         }
     })
    
    }
    

    在router-link解析之后,就需要来完成对应path下的component解析了,就是要改变视图,显示当前path下的视图
    在initComponents方法下继续添加一个组件来解析router-view,使用render函数,

      Vue.component('router-view',{
          render (h){
              //在routerMap下path和component是以键值对的形式存放的,所以有key值就能获取到对应的value也就是path对应component,例如:{'/',component},之后h函数进行渲染即可
              const component = _this.routerMap[_this.data.current]
              return h(component)
          }
      })
    

    完成工作之后就需要初始化调用

    init (){
        //解析options,生成键值对
        this.creatRouterMap()
        //解析router-link和router-view
        this.initComponents(vm);
       //监听url地址的变化,从而改变视图
        this.initEvent()
    }
    

    当然在完成以上所有还存的一个问题就是在点击浏览器前进/后退时,url是改变了,但是视图并没有改变,所以需要一个新的方法,就是监听url的变化,添加一个initEvent函数用来监听变化,将当前的路由地址存储到this.data.current中,在history模式下是通过监听popState,在事件处理函数中获取到pathname添加到this.data.current,在hash模式下是监听hashchange事件,事件处理函数中直接截取location.hash存放到this.data.current

    initEvent(){
        //监听popstate,改变url
        if(this.mode==='history'){
            window.addEventListener('popstate',()=>{
                this.data.current = window.location.pathname;
            });
        }else {
            window.location.hash = '/'
            //监听hashcahange
            window.addEventListener('hashchange',()=>{
                //获取到hash地址,截取
                this.data.current = window.location.hash.slice(1);
            })
        }
        
    }
    

    至此,一个简版的vue-router就完成了,当然考虑的还不是特别周到,所以有错误希望能的得到指点也好即使更正

    年华如果虚度,生命将毫无意义

    相关文章

      网友评论

          本文标题:简单实现vue-router

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