最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 再也不怕面试官了: vue router原理剖析, 自行实现router

    正文概述 掘金(EssionLi)   2021-06-01   422

    最近面试官总喜欢问全家桶原理, 所以有了这篇文章, vue-router原理剖析, 一边讲解原理一边自己实现.看完绝对能让大家有所收获

    我们正常使用router, 是在router.js里配置options, 并抛出Router实例

    import Vue from 'vue'
    import Router from 'vue-router'
    // 引入Router
    Vue.use(Router)
    
    const routes = [
        {
            path: '/',
            name: 'home'
        }
    ]
    // 抛出实例
    export default new Router({
        routes
    })
    
    

    然后在main.js中挂载Router实例

    import Vue from 'vue'
    import App from './App.vue'
    import router from './router'
    // 将带有配置项的Router实例挂载到Vue实例上
    new Vue({
        router,
        render: h => h(App)
    }).$mounted('#app)
    
    

    我们可以看到 一切的起点在于

    import Router from 'vue-router'
    Vue.use(Router)
    

    分析第一步, import进来什么东西, 或者说Vue插件是什么样的形式.

    答案是 函数或对象 并且里面一定有一个 install方法

    引入进来之后 Vue.use就是调用函数中的intall方法, 并且传入Vue构造函数, 为什么要传入呢, 因为方便我们修改Vue的原型, 起到扩展的作用

    下面用自己的代码写一个router类

    let Vue;
    class MiniRouter {
    }
    // 首先挂载install方法
    MiniRouter.install = funciton(_Vue) {
        Vue = _Vue
    }
    export default MiniRouter
    

    现在已经能被引用了, 但还没有任何功能, 我们想一下router都有什么功能

    1. 可以在Vue里使用this.router操作页面
    2. 可以使用<router-linke>标签和<router-view>标签
    3. 在url发生变化时, 页面内容发生变化

    首先先来说第一点, 让vue可以在组件中使用this.router, this.route, 那么就需要在this上挂载这两个对象

    如何去挂载? 我们很容易就能想到

    Vue.prototype.$router = {}
    Vue.prototype.$route = {}
    

    可不能是个空对象吧 应该是有相关配置的实例才对啊, 那么问题很明显, 我们需要Router实例

    再来看一下router.js

    import Vue from 'vue'
    import Router from 'vue-router'
    Vue.use(Router) // 在这里执行的install方法
    const routes = [
        {
            path: '/',
            name: 'home'
        }
    ]
    export default new Router({ // 在这里创建的Router实例
        routes
    })
    
    

    也就是说在install的时候我们还没有Router实例, 所以我们的目的是延时执行挂载代码,等到创建Router实例之后再执行. router的解决方案是利用Vue的全局混入, mixin, 这里很巧妙. 因为创建Vue实例时, 会把Router实例挂载到自身的options上 我们可以在组件里用this.$options.router访问

    let Vue;
    class MiniRouter {
    }
    // 首先挂载install方法
    MiniRouter.install = funciton(_Vue) {
        Vue = _Vue
        Vue.mixin({ // mixin中的函数在触发组件生命周期时才会执行, 此时已可以使用this, 指向组件实例
            beforeCreate() { 
                // 混入到beforeCreate生命周期中, 每个组件的这个生命周期都会执行, 但这显然不对
                // 我们只需要在根组件里执行一次挂载操作 我们可以利用this.$options.router, 
                // 因为只有根组件才会有
                if(this.$options.router) {
                    Vue.prototype.$router = this.$options.router
                }   
            }
        })
    }
    export default MiniRouter
    

    至此我们已经挂载了router实例到vue的$router上, 但没有任何功能

    接下来看第二点, 挂载全局组件<router-linke><router-view>

    // 首先挂载install方法
    MiniRouter.install = funciton(_Vue) {
        Vue = _Vue
        Vue.mixin({ // mixin中的函数在触发组件生命周期时才会执行, 此时已可以使用this, 指向组件实例
            beforeCreate() { 
                // 混入到beforeCreate生命周期中, 每个组件的这个生命周期都会执行, 但这显然不对
                // 我们只需要在根组件里执行一次挂载操作 我们可以利用this.$options.router, 
                // 因为只有根组件才会有
                if(this.$options.router) {
                    Vue.prototype.$router = this.$options.router
                }   
            }
        })
        // 注意正常项目中只能用render函数, 不能使用template
        // 因为正常项目是runtime环境,没有编译器, 走的是webpack的vue-loader
        Vue.component('router-link', {
            // <router-link to="about">jump</router-link>
            props: {
                to: { // 接受to属性, 必传参数
                    type: String,
                    required: true
                }
                
            }
            render(h) {
                // h即createElement, 返回vnode
                // router-link默认为a标签, 利用 this.$slots.default拿到标签中间的内容, 叫默认插槽
                // a标签应有href 跳转to指定地址, 
                return h('a', {
                     attrs: {
                         href: '#' + this.to
                     }
                }, this.$slots.default)
            }
        })
    }
    

    <router-link>实现了, 接下来是<router-view>

    router-view思路其实也简单, 其实就是通过地址匹配路由, 找到组件, 这个步骤在MiniRouter中完成

    let Vue;
    class MiniRouter {
        constructor(options) {
            // 传入的options中包含路由配置信息, 保存下来, 这样实例可以拿到配置
            this.$options = options
            // 接下来需要监听hashchange事件, 并做出相应
            // 如何做出响应? 我们需要一个响应式的数据, 通过改变这个数据的值, 做出响应
            // 我们这里使用defineReactive
            Vue.util.defineReactive(
                this, // MiniRouter构造函数
                'current', // 通过改变这个字段的值做出响应
                window.location.hash.slice(1) || '/' , 初始值
            )
                  
            window.addEventListener('hashchange', () => {
                // 拿到的hash前面带'#', slice处理, 去掉'#', 保存下来, 默认值为‘/’
                this.curremt = window.location.hash.slice(1) || '/' 
            })
        }
    }
    

    此时已经可以响应url的变化了

    Vue.component('router-view', {
        render(h) {
            let component = null; 
            // this.$router就是我们之前在Vue.prototype上挂载的Router实例 
            // 通过Router实例能获取到上一步保存的options
            // options里面有new实例的时候传入的routes配置表
            // 查找与上一步保存的当前path相同的项
            const router = this.$router.$options.routes
                                .find(route => route.path === this.$router.current)
            if (route) {
                // 如果有相同的 取出component组件
                component = route.component
            }
            // 渲染匹配的组件, 没有匹配到, 则默认值null, 不渲染
            return h (component)
        }
    })
    

    此时已经可以通过点击<router-link>切换path, 并渲染不同的组件了

    大功告成!

    恭喜你, 以后可以和面试官说自己可以手写vue-router了! 举一反三一下, 还可以写出其他插件.

    码字不易, 希望小伙伴能给个点赞收藏, 也是我以后更新的动力, 感谢大家!


    起源地下载网 » 再也不怕面试官了: vue router原理剖析, 自行实现router

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元