Vue-Router原理介绍,手写一个自己的Vue-Router

前置知识

前端路由的两种模式

  1. Hash模式
    URL中#后面的内容作为路径地址
    监听HashChange事件
    根据当前路由地址找到对应的组件重新渲染
  2. History模式
    通过history.pushState()方法改变地址栏
    监听popstate 事件
    根据当前路由地址找到对应组件重新渲染

手写路由的准备工作

新建一个没有vue-router的vue项目

在项目中新建router相关文件

  1. 新建一个route文件夹,文件夹下新建一个index.js,用于存放路由规则
import Vue from 'vue'
import VueRouter from '../vuerouter'
//从我们自己的router中导入VueRouter
import Index from '../components/Index.vue'
Vue.use(VueRouter)

const routes = [
    {
    
    
        path:'/',
        name:'Index',
        component : Index
    },
    {
    
    
        path:'/about',
        name:'about',
        component: ()=> import('@/views/about.vue')
    },
    {
    
    
        path:'/home',
        name:'home',
        component: ()=> import('@/views/home.vue')
    },
]
const router = new VueRouter({
    
    
    mode:'history',
    base: process.env.BASE_URL,
    routes
})

export default router
  1. 新建一个vuerouter文件夹,文件夹下新建一个index.js 用于存放手写的router代码
    在这里插入图片描述

  2. 别忘了在main.js中引用我们写到的router

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


Vue.config.productionTip = false

new Vue({
    
    
  router,
  render: h => h(App),
}).$mount('#app')

开始手写Vue-Router

首先要明确手写Vue-router要做什么,下图是Vue-router的一个类图,详细的记录了VueRouter需要什么属性与方法
在这里插入图片描述

options:记录构造函数中传入的对象(这个对象里面有路由规则)
routeMap:是一个对象,用来记录路由地址和组件的对应关系,将来会把路由规则解析到routeMap里
data:是一个对象,里面有一个属性current,用来记录当前路由地址,这里使用data 是为了实现一个响应式的对象。

install方法实现

通过类图中的_可以发现install是一个静态的方法

let _Vue = null

export default class VueRouter {
    
    

    static install (Vue) {
    
    
        //判断当前插件是否被安装
        if(VueRouter.install.installed){
    
    
            return;
        }
        VueRouter.install.installed = true

        // 把vue构造函数记录到全局变量
        _Vue = Vue
        // 把创建vue实例的时候传入的router对象注入到Vue实例上
        //混入 mixed
        _Vue.mixin({
    
    
            beforeCreate() {
    
    
            if(this.$options.router)
                _Vue.prototype.$router = this.$options.router
            },
        })
    }

}

构造函数的实现

    constructor(options){
    
    
        this.options = options

        this.routerMap = {
    
    } // 用于存储解析的router对象
        this.data = _Vue.observable({
    
    
            current:'/'
        })
        // this.init()
    }

这样就实现了类图中的相关属性

createRouterMap方法实现

这个方法的作用是把构造函数中传入的路由规则转变为键值对的形式,存储到上文提到的routeMap中

//遍历所有的路由规则,解析路由规则变为键值对的形式,存储到routermap里
    createRouterMap(){
    
    
        this.options.routes.forEach(route =>{
    
    
            this.routerMap[route.path] = route.component
        })
    }

initComponents方法实现

    initComponents (Vue) {
    
    
        Vue.component('router-link',{
    
    
            props:{
    
    
                to:String
                //接收外部传入的to
            },
            // template:'<a :href="to"><slot></slot></a>'
            render (h) {
    
    
                return h('a',{
    
    
                    attrs:{
    
    
                        href:this.to
                    },
                    on:{
    
    
                        click:this.clickHandler
                    }
                },[this.$slots.default])
            },
            methods: {
    
    
                clickHandler(e){
    
    
                    history.pushState({
    
    },'',this.to)
                    this.$router.data.current = this.to
                    e.preventDefault();
                }
            },
        })
        const  _this = this
        Vue.component('router-view', {
    
    
            render(h) {
    
    
                const component = _this.routerMap[_this.data.current]
                return h(component)
            },
        })
    }

initEvent 方法实现

initEvent(){
    
    
        window.addEventListener('popstate',()=>{
    
    
            this.data.current = window.location.pathname
        })
    }

这个方法是为了在浏览器中后退和前进的时候来加载并且渲染组件

完整手写路由代码

let _Vue = null

export default class VueRouter {
    
    

    static install (Vue) {
    
    
        //判断当前插件是否被安装
        if(VueRouter.install.installed){
    
    
            return;
        }
        VueRouter.install.installed = true

        // 把vue构造函数记录到全局变量
        _Vue = Vue
        // 把创建vue实例的时候传入的router对象注入到Vue实例上
        //混入 mixed
        _Vue.mixin({
    
    
            beforeCreate() {
    
    
            if(this.$options.router)
                _Vue.prototype.$router = this.$options.router
                // this.$options.router.init()
            },
        })
    }

    constructor(options){
    
    
        this.options = options

        this.routerMap = {
    
    } // 用于存储解析的router对象
        this.data = _Vue.observable({
    
    
            current:'/'
        })
         this.init()
    }
    init(){
    
    
        this.createRouterMap()
        this.initComponents(_Vue)
        this.initEvent()
    }
    //遍历所有的路由规则,解析路由规则变为键值对的形式,存储到routermap里
    createRouterMap(){
    
    
        this.options.routes.forEach(route =>{
    
    
            this.routerMap[route.path] = route.component
        })
    }
    initComponents (Vue) {
    
    
        Vue.component('router-link',{
    
    
            props:{
    
    
                to:String
            },
            // template:'<a :href="to"><slot></slot></a>'
            render (h) {
    
    
                return h('a',{
    
    
                    attrs:{
    
    
                        href:this.to
                    },
                    on:{
    
    
                        click:this.clickHandler
                    }
                },[this.$slots.default])
            },
            methods: {
    
    
                clickHandler(e){
    
    
                    history.pushState({
    
    },'',this.to)
                    this.$router.data.current = this.to
                    e.preventDefault();
                }
            },
        })
        const  _this = this
        Vue.component('router-view', {
    
    
            render(h) {
    
    
                const component = _this.routerMap[_this.data.current]
                return h(component)
            },
        })
    }
    initEvent(){
    
    
        window.addEventListener('popstate',()=>{
    
    
            this.data.current = window.location.pathname
        })
    }
}

参考资料:
拉勾教育

猜你喜欢

转载自blog.csdn.net/qq_43377853/article/details/113841827