手撕Vue-Router

1.Vue-Router本质

根据"不同的hash值“或者”不同的路径地址", 将不同的内容渲染到router-view
所以实现VueRouter的核心关键点就在于如何监听’hash’或’路径’的变化, 再将不同的内容写到router-view中

hash

	<a href="#/home">首页</a>
	<a href="#/about">关于</a>
	<div id="html"></div>
	// hashchange监听hash的变化
 	window.addEventListener('hashchange', ()=>{
        // console.log('当前的hash值发生了变化');
        let currentHash = location.hash.slice(1);
        console.log(location,currentHash);
        document.querySelector('#html').innerHTML = currentHash;
    })
    // 路径一开始就有hash,load 监听网络加载完成
    window.addEventListener('load', ()=>{
        let currentHash = location.hash.slice(1);
        document.querySelector('#html').innerHTML = currentHash;
    })

路径

	<a onclick="go('/home')">首页</a>
	<a onclick="go('/about')">关于</a>
	<div id="html"></div>
	function go(path) {
        console.log(path);
        history.pushState(null, null, path);
        document.querySelector('#html').innerHTML = path;
    }
    // popstate监听前进或者后退
    window.addEventListener('popstate', ()=>{
        console.log('点击了前进或者后退', location.pathname);
        document.querySelector('#html').innerHTML = location.pathname;
    })

history.pushState(参数1, 参数2,参数3)

参数1:对象
参数2:当前网络标题(会自动被所有浏览器忽略)
参数3:path(需要追加的路径)

手写vue-router

// 保存相关信息,方便后面注入(this.$router, this.$route)
class SueRouteInfo {
    constructor(){
        // 保存当前地址
        this.currentPath = null
    }
}
class SueRouter {
    constructor(options){
        this.mode = options.mode || 'hash' // 路由模式,未指定则默认为hash
        this.routes = options.routes || []
        /**
         * 改造配置路由
         * 可以根据路由地址获取到对应的组件
         * key:路由地址
         * value:组件
         */
        // 提取路由信息
        /**
         * {
         *  '/home': Home,
         *  '/about': About
         * }
         */
        this.routesMap = this.createRoutesMap()
        console.log(this.routes,this.routesMap);
        this.routeInfo = new SueRouteInfo()
        // 初始化默认的路由信息
        this.initDefault()
    }
    initDefault(){
        if(this.mode === 'hash'){
            console.log('location.hash', location.hash);
            // 1.判断打开的界面有没有hash, 如果没有就跳转到#/
            if(!location.hash){
                location.hash = '/'
            }
            // 2.加载完成之后和hash发生变化之后都需要保存当前的地址
            window.addEventListener('load', ()=>{
                this.routeInfo.currentPath = location.hash.slice(1)
            })
            window.addEventListener('hashchange', ()=>{
                this.routeInfo.currentPath = location.hash.slice(1)
            })
        }else {
            console.log('location.pathname', location.pathname);
            // 判断打开的界面有没有路径, 如果没有就跳转到/
            if(!location.pathname){
                location.pathname = '/'
            }
            // 加载完成之后和history发生变化之后都需要保存当前的地址
            window.addEventListener('load', ()=>{
                this.routeInfo.currentPath = location.pathname
            })
            window.addEventListener('popstate', ()=>{
                this.routeInfo.currentPath = location.pathname
            })
        }
    }
    createRoutesMap(){
        return this.routes.reduce((map, route)=>{
            map[route.path] = route.component
            return map
        },{})
    }
}
SueRouter.install = (Vue, options) => {
    Vue.mixin({
        beforeCreate(){
            console.log('this.$options', this.$options);
            /**
             * import router from './router'
             * this.$options.router 
             * new Vue({
                router, 
                render: h => h(App)
                }).$mount('#app')
             */
            if(this.$options && this.$options.router){
                this.$router = this.$options.router
                this.$route = this.$router.routeInfo
                Vue.util.defineReactive(this, 'xxx', this.$router);
                // 使用Vue.util.defineReactive的原因是 render在load之前执行
                // currentPath 双向绑定
            }else {
                this.$router = this.$parent.$router
                this.$route = this.$router.routeInfo
            }
        }
    })
     /*
    只要外界使用了Vue-Router, 那么我们就必须提供两个自定义的组件给外界使用
    只要外界通过Vue.use注册了Vue-Router, 就代表外界使用了Vue-Router
    只要接通通过Vue.use注册了Vue-Router, 就会调用插件的install方法
    所以我们只需要在install方法中注册两个全局组件(router-link router-view)给外界使用即可 // 
    * */
   Vue.component('router-link',{
        props:{
            to: String
        },
        render(){
            /**
             * 注意点: render方法中的this并不是当前实例对象, 而是一个代理对象
                    如果我们想拿到当前实例对象, 那么可以通过this._self获取
             */
            /**
             * this._self 才能拿到当前Vue实例
             */
            // history / hash:this._self.$router.mode
            let path = this.to // this.to不能直接修改
            if(this._self.$router.mode === 'hash'){
                path = '#' + path
            }
            return <a href={path}>{this.$slots.default}</a> // 获取内容 this.$slots.default
        }
   })
   Vue.component('router-view', {
       render(h){
           let routesMap = this._self.$router.routesMap;
           let currentPath = this._self.$route.currentPath;
           let currentComponent = routesMap[currentPath]
           return h(currentComponent)
       }
   })
}
export default SueRouter;

学习记录❥(^_-)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值