一文读懂利用vue插件原理手写一个简单版本的vue-router

简单分析过程


源码如下:

1、创建一个vue-router.js文件

// 声明变量,缓存Vue实例对象
let Vue

/**
 * 声明VueRouter类
 */
class VueRouter {
    constructor(options) {
        this.$options = options

        // 创建一个路由path和route映射
        this.routeMap = {}

        // 当前路径current需要响应式-利用vue响应式实现
        this.app = new Vue({
            data: {
                current: '/'
            }
        })
    }

    init() {
        // 绑定浏览器事件,监听路由事件
        this.bindEvents()

        // 解析传入的路由配置-routes
        this.createRouteMap(this.$options);

        // 声明两个全局组件:router-link 和 router-view
        this.initComponent()
    }

    bindEvents() {
        window.addEventListener('hashchange', this.onHashChange.bind(this))
        window.addEventListener('load', this.onHashChange.bind(this))
    }

    onHashChange() {
        // http://localhost/#/home
        this.app.current = window.location.hash.slice(1) || '/'
    }

    createRouteMap(options) {
        // 遍历,然后缓存
        options.routes.forEach(item => {
            // ['/home']: {path: '/home', component: Home}
            this.routeMap[item.path] = item
        })
    }

    initComponent() {
        // 声明两个全局组件
        Vue.component('router-link', {
            props: {
                to: String
            },
            render(h) {
                // 目标是: <a href="to">XXX</a>
                return h('a', {attrs: {href: '#' + this.to}}, this.$slots.default)
                // return <a href={this.to}>this.$slots.default</a>
            }
        })

        // hash -> current -> render
        Vue.component('router-view', {
            // 箭头函数能保留this指向,这里指向VueRouter实例
            render: h => {
                const Comp = this.routeMap[this.app.current].component
                return h(Comp)
            }
        })
    }

}

// 把VueRouter变为插件-必须实现install
VueRouter.install = function(_vue) {
    // 保存vue实例
    Vue = _vue

    // 混入任务-混入就是扩展Vue
    Vue.mixin({
        beforeCreate() {
            // 这里的代码将来会在外面初始化的时候被调用
            // 这样实现了Vue扩展
            // this指向Vue组件实例
            // 这里只希望根组件执行一次
            if (this.$options.router) {
                Vue.prototype.$router = this.$options.router
                // 初始化
                this.$options.router.init()
            }
        }
    })
}

export default VueRouter

2、创建main.js

import Vue from "vue";
import App from "./App.vue";
import VueRouter  from './vue-router';

//以插件的形式,使用VueRouter
Vue.use(VueRouter);

//路由配置信息,可以从外部文件引入,在此直接写是为了方便演示
const Home = { template: '<div>home</div>' }
const About = { template: '<div>about</div>' }
const routes = [
  { path: '/', component: Home },
  { path: '/about', component: About }
]

//初始化并与 Vue 实例关联
const router = new VueRouter({routes});
new Vue({
  router,
  render: h => h(App),
}).$mount("#root");

3、创建App.vue-用于测试

<template>
    <div>
        <h1 @click="goBack">App Test</h1>
        <router-link to="/">Home</router-link>
        <router-link to="/bar">About</router-link>
        <router-view></router-view>
    </div>
</template>

<script>
export default {
  methods: {
    goBack() {
      console.log(this.$router);  
      window.history.length > 1 ? this.$router.go(-1) : this.$router.push('/')
    }
  }
}
</script>

<style lang="less" scoped>

</style>
标签: