手写简版vue-router@3.xx

45 阅读1分钟

1.手写vue-router源码

let Vue = null

class HistoryRoute {
    constructor() {
        this.current = null
    }
}

class VueRouter {
    constructor(options) {
        this.mode = options.mode || 'hash'
        this.routes = options.routes || []
        this.routesMap = this.createMap(this.routes) // 路由-组件平铺
        this.history = new HistoryRoute()
        this.init()
    }
    createMap(routes) {
        return routes.reduce((pre, current) => {
            pre[current.path] = current.component
            return pre
        }, {})
    }
    init() {
        // hash模式
        if (this.mode === 'hash') {
            location.hash ? "" : (location.hash = '/')
            const handleRouter = () => {
                this.history.current = location.hash.slice(1)
            }
            // load事件,防止首次进入页面时,无法触发对应的hashchange和popstate事件
            window.addEventListener('load', handleRouter)
            window.addEventListener('hashchange', handleRouter)
        }
        else {
            location.pathname ? "" : (location.pathname = '/')
            const handleRouter = () => {
                this.history.current = location.pathname
            }
            window.addEventListener('load', handleRouter)
            window.addEventListener('popstate', handleRouter)
        }
    }
}

VueRouter.install = function (v) {
    Vue = v // 获取vue
    Vue.mixin({
        beforeCreate() {
            // 根节点
            if (this.$options && this.$options.router) {
                this._root = this
                this._router = this.$options.router
                Vue.util.defineReactive(this, 'vuerouter', this._router.history)
            }
            else {
                // 子组件
                this._root = this.$parent && this.$parent._root
            }
            Object.defineProperty(this, '$router', {
                get() {
                    return this._root._router
                }
            })
            Object.defineProperty(this, '$route', {
                get() {
                    return this._root._router.history.current
                }
            })

        }
    })
    // 定义导航组件
    Vue.component('router-link', {
        props: {
            to: String
        },
        render(h) {
            const mode = this._self._root._router.mode
            const to = mode === 'hash' ? '#' + this.to : this.to
            return h('a', {
                attrs: { href: '' }, on: {
                    click: (e) => {
                        e.preventDefault()
                        window.history.pushState(null, null, to)
                        // 因为pushState不会触发popstate/hashchange事件
                        // 这里采用手动触发
                        const eventKey = mode == 'hash' ? 'hashchange' : 'popstate'
                        const event = new Event(eventKey);
                        window.dispatchEvent(event);
                    }
                }
            }, this.$slots.default)
        }
    })
    Vue.component('router-view', {
        render(h) {
            console.log('this', this);
            const current = this._self._root._router.history.current
            const comp = this._self._root._router.routesMap[current]
            return h(comp)
        }
    })
}


export default VueRouter

2.使用

// import VueRouter from "vue-router";
import VueRouter from './core'
import Vue from "vue";
import HomeVue from '../view/home-vue.vue'
import AboutVue from '../view/about-vue.vue'
Vue.use(VueRouter)


const routes = [
    // {
    //     path: '/',
    //     redirect: '/home'
    // },
    {
        path: '/home',
        component: HomeVue
    },
    {
        path: '/about',
        component: AboutVue
    }
]

const router = new VueRouter({
    mode: 'history',
    routes,
})


export default router
// vue 文件
<template>
  <div id="app">
    <router-link to="/home">home</router-link>
    &nbsp;&nbsp;
    <router-link to="/about">about</router-link>
    <router-view></router-view>
  </div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue'

export default {
  name: 'App',
  components: {
    HelloWorld
  },
  beforeCreate() {

  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  margin-top: 60px;
}
</style>

3.页面效果

image.png