一文搞懂vue-router的使用与实现

139 阅读1分钟

大家好,我是草舟,跟我一起学前端👋。

今天,我们从基本使用开始,理解并实现一个vue-router

1.1 Vue Router使用步骤

router/index.js

// 【第一步】导入vue、vue-router
import Vue from 'vue'
import VueRouter from 'vue-router'

// 【第二步】注册路由插件 
Vue.use(VueRouter) // use方法如果传入函数-直接调用函数,如果传入对象-调用对象的install方法

// 路由规则
const routes = [
    {
    	path: '/',
        name: 'Index',
        component: () => import(/* webpackChunkName: "xxx" */ '../views/xxx.vue')
    }
    ...
]

// 【第三步】创建router对象
const router = new VueRouter({
    mode: '', // 默认hash , 可选history
    base: xxx, // 基地址
    routes
})

main.js

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

new Vue({
    // 【第四步】 注册router对象(给Vue实例注入$route和$router)
    router,
    render: h => h(App)
}).$mount('#app')

App.vue

// 【第六步】创建链接
<router-link to="/"> Index </router-link>
// 【第五步】创建路由组件的占位
<router-view />

1.2 Hash模式和History模式的区别

表现形式的区别:

原理上的区别:

  • Hash 模式是基于锚点,以及 onhashchange 事件
  • History 模式是基于HTML5的History API
    • history.pushState() IE10后才支持
    • history.replaceState()

1.3 VueRouter-分析

  1. 【第一步】实现router/index.js中引入的vue-router文件
  2. 【第二步】实现注册路由插件调用的install方法
  3. 【第三步】利用混入,把创建Vue实例时候传入的router对象注入到Vue实例上
  4. 【第四步】编写createRouteMap方法,遍历所有的路由规则,解析成键值对的形式,存储到routeMap中
  5. 【第五步】编写initComponents方法来创建router-link和router-view组件
  6. 【第六步】编写initEvent方法监听路由返回触发的history的popstate方法改变当前路由地址

1.4 VueRouter-实现

let _Vue = null
export default class VueRouter {
    static install (Vue) {
    	//1. 判断当前插件是否已经被安装
        if(VueRouter.install.installed) {
            return
        }
        VueRouter.install.installed = true
        //2. 把Vue构造函数记录到全局变量
        _Vue = Vue
        //3. 把创建Vue实例时候传入的router对象注入到Vue实例上
        // _Vue.prototype.$router = this.$options.router
        // 混入
        _Vue.mixin({
            beforeCreate() {
                if(this.$options.router) {
                    _Vue.prototype.$router = this.$options.router
                    this.$options.router.init()
                }
            }
        })
    }
    
    // 构造函数
    constructor(options) {
    	this.options = options
        this.routeMap = {}
        this.data = _Vue.observable({
            current: '/' // 当前路由默认地址
        })
    }
    
    init() {
        this.createRouteMap()
        this.initComponents(_Vue)
        this.initEvent()
    }
    
    // createRouteMap
    createRouteMap() {
    	// 遍历所有的路由规则,解析成键值对的形式,存储到routeMap中
        this.options.routes.forEach(route => {
            this.routeMap[route.path] = route.component
        })
    }
    
    // initComponents
    initComponents(Vue) {
    	Vue.component('router-link', {
            props: {
                to: String
            },
            render(h) {
                return h('a', {
                        attrs: {
                            href: this.to
                        },
                        on: {
                            click:this.clickHandler
                        }
                    }, [this.$slots.default])
                },
                methods: {
                    clickHandler (e) {
                        // 调用pushState(data, title, url)方法改变浏览器地址栏,但不会向服务器发送请求
                        history.pushState({}, '', this.to)
                        this.$router.data.current = this.to
                            e.preventDefault()
                    }            
                }
        })
        
        const self = this
        Vue.component('router-view', {
            render(h) {
                const component = self.routeMap[self.data.current]
                return h(component)
            }
        })
    }
    
    initEvent() {
    	Window.addEventListener('popstate', () => {
            this.data.current = window.location.pathname
        })
    }
}