实现一个简易的 vue router

实现一个简易的 vue router

1.vue-router 有2种模式 hash 与 history 模式,hash模式就是利用锚点的原理实现跳转(通过修改window.location.hash 值),而history 通过修改浏览器访问历史栈来实现(该模式需要后端支持)。

2.由于History 需要后端支持,这里就不做实现,这里主要实现一下hash 模式路由。

// 这里先引入 vue 库的 cdn
 <script crossorigin="anonymous" integrity="sha384-+jvb+jCJ37FkNjPyYLI3KJzQeD8pPFXUra3B/QJFqQ3txYrUPIP1eOfxK4h3cKZP" src="https://lib.baomitu.com/vue/2.6.11/vue.js"></script>

接下来我们先写几个全局组件用于验证使用

let homePage = Vue.component('home-page',{
        data:()=>({
            title:'this is home page!',
            message:'welcome go to home page!'
        }),
        methods:{
            goToNews(){
                router.replace({path:'/news',query:{name:'I am home page',action:'I am to go news page'}});
            },
            goToStore(){
                router.replace('/store');
            }
        },
        template:`
            <div>
                <h2>{{title}}</h2>
                <p>{{message}}</p>
                <button @click="goToNews">click me go to news page</button>
                <button @click="goToStore">click me go to store page</button>
            </div>
        `
    });
let newsPage = Vue.component('news-page',{
        data:()=>({
            title:'this is news page!',
            message:'welcome go to news page'
        }),
        methods:{
            goToHome(){
                router.push({path:'/',query:{name:'I am news page',action:'I am to go home page'}});
            }
        },
        template:`
            <div>
                <h2>{{title}}</h2>
                <p>{{message}}</p>
                <button @click="goToHome">click me go to home page</button>
            </div>    
        `
    });
    let storePage = Vue.component('store-page',{
        data:()=>({
            title:'this is store page!',
            message:'welcome go to store page'
        }),
        methods:{
            goToNews(){
                router.push('/news');
            }
        },
        template:`
            <div>
                <h2>{{title}}</h2>
                <p>{{message}}</p>
                <button @click="goToNews">click me go to news page</button>
            </div>    
        `
    });
    // 路由数组
    const routes = [
        {
            path:'/',
            component:homePage
        },
        {
            path:'/news',
            component:newsPage
        },
        {
            path:'/store',
            component:storePage
        }
    ]

class Router{

        constructor(options){
            //  用于存储路径与组件之间当映射关系的仓库
            this.routeMap = {};
            //  创建一个vue 实例对象 用于 当路由改变时 触发 试图更新
            this.app = new Vue({
                data:{
                    currentPath:'/'
                }
            });
            //初始化 路由配置
            this.init(options);
            //初始化 router-link  router-view 组件
            this.initComponent();
        }
        init(options){
            // 监听 hashchange 事件 来监控 hash 值改变
            window.addEventListener('hashchange',this.onHashChange.bind(this));
            // 为每个路由 建立 映射关系
            options.routes.forEach(route => {
                this.routeMap[route.path] = route.component;
            });
            //当浏览器 刷新时需要重置 路由
            window.onload=function(){
                window.location.hash = '/'
            }
        }
        // 当hash 值改变时触发的回调函数
        onHashChange(){
            let path = this.getHash();
            // 判断 hash 值中是否有? (当路由进行query传参来就会出现?)
            if(path.includes('?')){
                let index = path.indexOf('?');
                // 只需要hash 值   ? 之前当字符串,因为我们注册当路由中并没有写?而是纯粹当路径
                this.app.currentPath = path.slice(0,index);
            }else{
                this.app.currentPath = path;
            }           
        }
        getHash(){
            return window.location.hash.slice(1) || '/';
        }
        setHash(type,url){
            if(type === 'push') window.location.hash = url;
            else if(type === 'replace'){
                if(window.location.hash.includes('#')){
                    let index = window.location.hash.indexOf('#')+1;
                    url = window.location.hash.slice(0,index) +url;
                }else{
                    url = window.location.hash + url;
                }
                
                window.location.replace(url);
            }
        }
        initComponent(){
            let _this = this;
            Vue.component('router-link',{
                props:{
                    to:[String,Object]
                },
                template:`
                    <a :href="to">
                        <slot></slot>
                    </a>
                `
            });
            Vue.component('router-view',{
                render(h){

                    let component = _this.routeMap[_this.app.currentPath];
                    return h(component);
                }
            });
        }
        push(){
            this.setLocation('push',arguments[0]);
        }
        replace(){
            this.setLocation('replace',arguments[0]);
        }
        setLocation(type,arg){
            if(typeof arg === 'string'){
                this.setHash(type,arg);
            }else if(Object.prototype.toString.call(arg) === "[object Object]"){
                let url = arg.path;
                // 当传来参数时 对 url 进行拼接
                if(arg.query){
                    url += '?';
                    for(let attr in arg.query){
                        url += `${attr}=${arg.query[attr]}&`;
                    }
                    // 去除最后多余当一个 & 符号
                    let index = url.length -1;
                    url = url.slice(0,index);
                }
                this.setHash(type,url);
            }
        }
    }

    const router = new Router({routes});
    new Vue({
        el:'#app',
        router
    })

html 部分

 <div id="app">
        <div>
            <router-link to="#/">首页</router-link>
            <router-link to="#/news">新闻</router-link>
            <router-link to="#/store">商店</router-link>
        </div>
        <router-view></router-view>
   </div>

效果图:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值