vue-router原理简单实现

需求分析

  • 作为一个插件存在:实现VueRouter类和install方法。
  • 实现两个全局组件:router-view用于显示匹配组件内容,router-link用于跳转。
  • 监控url变化:监听hashchange或popstate事件。
  • 响应最新url:创建一个响应式的属性current, 当它改变时获取对应组件并显示。

一、编写一个MVueRouter插件

在vue2中使用vue-router需要单独安装引入,使用方式如下

import Vue from 'vue'
import VueRouter from 'vue-router'
import routes from './route/index'

Vue.use(VueRouter)

// 创建 router 实例,然后传 `routes` 配置
const router = new VueRouter({
  routes
})

// 创建和挂载根实例。
// 通过 router 配置参数注入路由,从而让整个应用都有路由功能
const app = new Vue({
  router
}).$mount('#app')

安装后需要通过Vue.use()注册,这说明可以把vue-router作为一个插件。

mvue-router.js

// 实现一个插件:挂载$router
class MVueRouter {
    install(_vue) {
        let Vue = _vue
        // 挂载$router
        // 怎么获取根实例中获取router选项
        // 利用全局或如,在beforCreate钩子里面获取选项
        Vue.mixin({
            // router只有在根实例中存在
            beforeCreate () {
                if (this.$options.router) {
                    Vue.prototype.$router = this.$options.router
                }
            }
        })

    }
}

export default MVueRouter

二、实现两个全局组件

这一步要实现两个全局的组件,<mvue-link></mvue-link>用于路由的跳转,<mvue-view></mvue-view>用于动态的的替换展示路由地址对应的组件。

mrouter-link.js

// router-link 组件实质上就是一个a标签,可以触发路由change事件
export default {
    props: {
        // to 属性: 跳转的url
        to: {
            type: String,
            require: true
        }
    },
    render(h) {
        // 渲染结果: <a href="/path">aaa</a>
        // 渲染函数三个参数: 标签名称、属性集合、子元素数组
        return h('a', { attrs: { href: `#${this.to}` } }, [this.$slots.default])
        // JSX语法
        // return <a href={'#' + this.to}>{this.$slot.default}</a>
    }
}

mrouter-view.js 

export default {
    
    render(h) {
        let component = null; // 需要渲染的组件

        // 动态获取需要渲染的组件
        ...

        return h(component)
    }
}

在mvue-router.js中全局注册这两个组件

import Link from './mrouter-link'
import View from './mrouter-view'

// 实现一个插件:挂载$router
class MVueRouter {
    ...
}

MVueRouter.install = function (_vue) {
        let Vue = _vue
        // 挂载$router
        // 怎么获取根实例中获取router选项
        // 利用全局或如,在beforCreate钩子里面获取选项
        Vue.mixin({
            // router只有在根实例中存在
            beforeCreate () {
                if (this.$options.router) {
                    Vue.prototype.$router = this.$options.router
                }
            }
        })

        // 声明两个全局组件
        Vue.component('mrouter-link', Link)
        Vue.component('mrouter-view', View)

}

export default MVueRouter

三、监听hashchange事件,管理url变化

  • 在url发生变化时,在hashchange事件中取出当前的路由path
  • 设置一个响应式的current属性与当前路由path一一映射
  • 当current发生变化时,就可以触发依赖他的函数

mrouter-router.js主要代码 

constructor(options) {
        // 把options通过$options暴露出去 方便外部使用this.$router.$options
        this.$options = options;

        // 设置一个响应式的current属性
        // Vue.util.defineReactive: 定义一个对象的响应属性
        /**
         * Vue.util.defineReactive(obj,key,value,fn)
                obj: 目标对象,
                key: 目标对象属性;
                value: 属性值
                fn: 只在node调试环境下set时调用
         */
        Vue.util.defineReactive(this, 'current', '/')

        // 监听hashchange事件
        window.addEventListener('hashchange', this.onHashChange.bind(this))
        // 加载的时候也执行一下
        window.addEventListener('load', this.onHashChange.bind(this))
        
    }

    onHashChange() {
        // 截取出#后面的path
        this.current = window.location.hash.slice(1)
    }

四、响应url的变化,渲染url对应的组件

前几步实现了路由的监听,并且将url进行了响应式处理,接下来就是对url对应的组件渲染出来,实现路由的跳转变换。

接下里在mrouter-view组件中实现

export default {
    
    render(h) {
        let component = null; // 需要渲染的组件

        // 动态获取current对应的组件
        // this指向当前实例
        // $router.$options.routes 即: 路由配置数组
        // $options是MVueRouter类暴露出来的option属性
        this.$router.$options.routes.forEach(route => {
            if (route.path === this.$router.current) {
                component = route.component
            }
        });

        return h(component)
    }
}

<mrouter-view></mrouter-view>组件中动态获取$router中的current属性,current属性与roures路由配置进行匹配,从而动态拿到对应的组件。current属性是一个响应式的,与当前的路由一一映射,当它发生变化时自然会触发组件的渲染。

扫描二维码关注公众号,回复: 14850252 查看本文章

五、测试

App.vue 

<template>
    <div id="app">
      <mrouter-link to="/403">403</mrouter-link>
      <mrouter-link to="/404">404</mrouter-link>
      <mrouter-view></mrouter-view>
      <!-- <router-view></router-view> -->
    </div>
</template>

router.js

import Vue from 'vue'
import MVueRouter from './plugin/mvue-router'
import routes from './route/index'

Vue.use(MVueRouter)

// 创建 router 实例,然后传 `routes` 配置
const router = new VueRouter({
  routes
})

// 创建和挂载根实例。
// 通过 router 配置参数注入路由,从而让整个应用都有路由功能
const app = new Vue({
  router
}).$mount('#app')

猜你喜欢

转载自blog.csdn.net/weixin_45032067/article/details/126571137