Vue Router 的简单实现

313 阅读1分钟

1.分析

Vue Router 有两种常用的模式:hash模式和history模式。这里首先尝试下用hash模式封装一个简单版的Vue Router。

  • hash模式: 使用url#后面的锚点来区分组件,hash改变的时候,页面不会重新加载,只会触发onhashchange事件
  • history模式: 这种模式充分利用了html5 history interface 中新增的 pushState() 和 replaceState() 方法。这两个方法应用于浏览器记录栈, 在当前已有的 back、forward、go 基础之上,它们提供了对历史记录修改的功能。只是当它们执行修改时,虽然改变了当前的 URL ,但浏览器不会立即向后端发送请求。

2.实现

let Vue
class Router {
    static install(_Vue) {
        Vue = _Vue
        Vue.mixin({
            beforeCreate() {
                if(this.$options.router) {
                    Vue.prototype.$flyRouter = this.$options.router
                    this.$options.router.init()
                }
            }
        })
    }
    constructor(options) {
        this.$options = options
        this.routeMaps = {}
        this.app = new Vue({
            data: {
                currentUrl: '/'
            }
        })
    }
    init() {
        this.bindEvents()
        this.generateHashMap()
        this.createComponent(Vue)
    }
    bindEvents() {
        window.addEventListener('hashchange', this.hashChangeEvent.bind(this), false)
        window.addEventListener('load', this.hashChangeEvent.bind(this), false)
    }
    hashChangeEvent(e) {
        let hash = this.getHash()
        let { from, to } = this.getFrom(e)
        let router = this.routeMaps[hash]
        if(router.beforeEnter) {
            router.beforeEnter(to, from, () => {
                this.app.currentUrl = hash
            })
        }else {
            this.app.currentUrl = hash
        }
    }
    getFrom(e) {
        let from = e.oldURL || ''
        let to = e.newURL || '/'
        return  {
            from,
            to
        }
    }
    getHash() {
        return window.location.hash.substr(1) || '/'
    }
    push(url) {
        window.location.hash = url
    }
    generateHashMap() {
        this.$options.routes.forEach(item => {
            this.routeMaps[item.path] = item.component
        })
    }
    createComponent(Vue) {
        const that = this
        Vue.component('router-link',{
            render(h) {
                return h('a', {
                        attrs: {
                            href: '#' + this.to
                        }
                    },
                    [this.$slots.default]
                )
            },
            props: {
                to: String
            }
        })
        Vue.component('router-view', {
            render(h) {
                let component = that.routeMaps[that.app.currentUrl]
                return h(component)
            }
        })
    }
}
export default Router

3.应用

在router/index.js里引用自定义的router.js

import Vue from 'vue'
import Router from '@/utils/router'

import Home from '@/pages/Home.vue'
import About from '@/pages/About.vue'

Vue.use(Router)

export default new Router({
    routes: [
       {
           path: '/',
           component: Home
       },
       {
            path: '/about',
            component: About,
            beforeEnter: (to,from,next) => {
                console.log('输出',to,from)
                setTimeout(() => {
                    next()
                }, 3000)
            }
       }
    ]
})

App.vue里的代码

<template>
  <div id="app">
      <router-link to="/about">去about页</router-link>
      <router-link to="/">来home页</router-link>
    
      <router-view></router-view>
  </div>
</template>

main.js里的代码

import Vue from 'vue'
import App from './App'
import router from '@/router'

Vue.config.productionTip = false

new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>'
})

4.总结

从以上代码可以看出,自定义的router已经基本实现了Vue Router的功能,其用法和Vue Router一致。