手写Vue Router 路由简单原理

Vue Router 是 Vue.js 官方的路由管理器。它和 Vue.js 的核心深度集成,让构建单⻚面应用变得易如反掌。

核心步骤:
步骤一:使用vue-router插件,router.js
import Router from 'vue-router'
Vue.use(Router)

步骤二:创建Router实例,router.js 

export default new Router({...})

步骤三:在根组件上添加该实例,main.js 

import router from './router'
new Vue({
  router,
}).$mount("#app");

步骤四:添加路由视图,App.vue 

<router-view></router-view>

导航 

<router-link to="/">Home</router-link>
<router-link to="/about">About</router-link>

vue-router源码实现 

需求分析

  • 作为一个插件存在:实现VueRouter类和install方法

  • 实现两个全局组件:router-view用于显示匹配组件内容,router-link用于跳转

  • 监控url变化:监听hashchange或popstate事件

  • 响应最新url:创建一个响应式的属性current,当它改变时获取对应组件并显示

 

源码:

/* 路由器插件 */
/* Vue.use(VRouter) */
let Vue

class VRouter {
    constructor(options) {
        this.$options = options

        // 缓存path和route映射关系
        this.routeMap = {}
        this.$options.routes.forEach(
            route => {
                this.routeMap[route.path] = route
            })

        // 响应式数据,响应式数据依赖于vue
        // current保存当前url
        // defineReactive给obj定义一个响应式属性 #/about
        const initial = window.location.hash.slice(1)
        Vue.util.defineReactive(this, 'current', initial) // this指向为router实例

        // 3.监控url变化
        window.addEventListener('hashchange', this.onHashChange.bind(this))
    }

    onHashChange () {
        this.current = window.location.hash.slice(1)
    }

}

// 1.实现install方法,注册$router和两个路由全局组件
VRouter.install = function (_Vue) {
    // 引用构造函数,VRouter中要使用
    Vue = _Vue

    // 挂载router实例,让我们的子组件可以使用它
    // 为了解决install先执行,还要在这里访问router实例(此时还未生成router实例)
    // 做一个全局混入,在beforCreate钩子里面做这件事
    Vue.mixin({
        beforeCreate () {
            // 此时上下文已经是组件实例
            // 如果this是根实例,则它的$options里面会有路由实例
            if(this.$options.router) {
                Vue.prototype.$router = this.$options.router
            }
        }
    })

    // 2.实现两个全局组件: router-link , router-view
    // 输入:<router-link to="/about">xxx</router-link>
    // 输出:<a href="#/about">xxx</a>
    Vue.component('router-link', {
        props: {
            to: {
                type: String,
                required: true
            }
        },
        render(h) {
            return h('a', {
                attrs: {
                    href: '#' + this.to
                }
            },
                [this.$slots.default])
        }
    })

    Vue.component('router-view', {
        render(h) {
            // 找到当前url对应组件
            const { current, routeMap } = this.$router
            const component = routeMap[current] ? routeMap[current].component : null

            // 渲染传入组件
            return h(component)
        }
    })
    
}

export default VRouter

  

 

 

 

 

 

posted @ 2020-09-02 14:37  不务正业的咸鱼  阅读(250)  评论(0)    收藏  举报