手写vueRouter源码

拨云见日

在这里插入图片描述

本篇文章主要是介绍如何手写vueRouter,希望可以帮助到大家,大家先看一下上面的思维导图
手写vueRouter源码
//初始化一个变量,为接下来保存当前的vue做准备
let _Vue = null

export default class VueRouter {

    static install(Vue) {

        //要传递两个参数一个是vue构造函数,另外一个是可选的选项对象

        //选项对象现在不需要,就不传递了

        //这个install()方法要做两件事情

        //1、判断当前插件是否已经被安装,如果安装了,就不需要再次安装了

        //2、把vue的构造函数记录到全局变量中来,因为当前install()方法是一个静态方法
        
        //在install()方法中接受了一个vue的构造函数,将来在vue的实例方法中还要使用这个构造函数,

        //3.把创建vue实例时传入的router对象传入所有的vue实例上,
        //以前所使用的router 就是在这个时候传入的,并且所有的组件都是vue的实例

        //逻辑判断当前这个vue插件是否被安装,如果已经安装的阻止代码执行
        if (VueRouter.install.installed) {
            return
        }
        //在构造函数中设置一个布尔值,用于标记当前插件是否被安装
        VueRouter.install.installed = true

        //把vue的构造函数记录到全局变量中
        _Vue = Vue

        //混入机制,在插件里面给所有vue-router设置一个选项
        //在beforeCreate这个生命周期中设置这个选项
        _Vue.mixin({
            //将来所有的组件和实例都会执行这个函数
            beforeCreate() {
                //逻辑判断,仅仅是实例需要挂载,所以增加一层逻辑判断
                if (this.$options.router) {
                    //此时this指向vue的实例,如果是实例就挂载
                    _Vue.prototype.$router = this.$options.router;
                    this.$options.router.init();
                }
            }
        })
    }

    constructor(options) {
        //用于记录当前传入的options
        this.options = options

        //将this.options传入的路由规则解析成键值对,键对应的就是路由地址,值就是对应的路由组件
        //,将来在route-view根据routeMap里面对应的地址找到对应的组件,然后渲染到浏览器中
        this.routeMap = {};

        //data是一个响应式的对象,存储当前的路由地址,路径发生变化的时候自动加载组件
        //所以data是响应式的对象,在vue中提供了一个observable()方法,这个方法用来创建响应式的对象
        this.data = _Vue.observable({
            current: '/',//用来存储当前的路由地址
        })
    }
    init() {
        //调用已经创建好的方法
        this.createRouteMap();
        this.initComponents(_Vue);
        this.initEvent()
    }
    createRouteMap() {
        //这个函数的作用就是,把this.options传入的路由规则解析成键值对的形式,存储到routeMap中

        //实现,通过遍历这个数组,然后存储到routeMap中
        this.options.routes.forEach(route => {
            this.routeMap[route.path] = route.component
        })

    }
    initComponents(Vue) {
        const self = this
        //创建两个组件分别是router-link / router-view

        //通过vue.component创建组件,第一个是组件的名字,第二个是组件的选项
        //接收外部传入的参数
        Vue.component('router-link', {
            props: {
                to: String
            },
            //设置最后被渲染成的标签, 通过‘:’设置属性,属性值是传入的to属性
            //slot插槽,将老router-link中内容自动替换slot
            //   template: "<a :href='to'><slot></slot></a>",
            render(h) {

                //render函数是一个渲染函数,参数一般是一个函数,一般以h命名
                //h是一个函数,帮我们创建虚拟dom
                //render调用这个函数,并且把h函数的结果返回,这个h函数是vue传递进去的

                //h函数可以传3个参数,第一个参数我们创建这个元素对应的选择器,可以使用标签选择器

                //第二个参数对应的属性给我们创建dom元素设置属性,可以通过attrs进行设置

                //第三个参数是所生成元素的子元素,里面是可以是很多内容所以是数组形式
                return h('a', {//这里渲染成a标签
                    attrs: {
                        href: this.to,//这里的属性值是传递过来的this.to

                    },
                    on: {
                        click: this.clickHandler,//后面不用加()而此时仅仅是注册事件,并不是执行
                    }
                }, [this.$slots.default])//获取slot的值

                //现在将定义好的vue-router引入的话会直接报错,很vue的构建版本有关系

                //vue的构建版本有两种一种是运行时版本,还有一种是完成版本

                //运行时版本:不支持template模板,需要打包时提前编译

                //完整版:包含运行时和编辑器,体积比运行时版大10K左右,程序运行的时候把模板转换成render函数

                //我这里才用的是cli创建的项目,采用的是运行时版本

                //解决方法如下完整版vue解决方法如下

                //项目的根目录下创建vue.config.js,内容如下
                // module.exports = {
                //     runtimeCompiler:true
                // }

                //运行时版本解决方法如下需要手动去写render函数

                //完整版本的vue包含运行时版本和编译器,

                //运行时版本的vue需要自己手动编写编译器,运行时版本的vue不支持template

                //完整版本的vue就是把template编译成render函数
            },
            methods: {
                clickHandler(e) {
                    //  history.pushState改变地址栏中的路径但是不会向服务器发送请求
                    //注意这里是仅仅改变路由地址

                    // history.pushState这个方法有3个参数,第一个是data 
                    //data就是将来触发popstate(),传给popstate事件的事件对象的参数
                    //在这里没有用到所以写一个空对象,
                    //第二个是title也就是网页的标题 
                    //第三个是url,也就是跳转的地址,但是这个地址,存在this.to里面
                    //直接写入this.to
                    history.pushState({}, "", this.to)
                    //根据路由路径改变路由对应的组件
                    this.$router.data.current = this.to
                    //阻止默认事件
                    e.preventDefault();
                }
            },
        },
        )
        Vue.component('router-view', {

            render(h) {

                //在这里呢需要根据当前的路由地址,到routerMap找到路由地址对应的组件

                //然后使用函数把组件转换成虚拟dom返回,当前路由地址存储在this.data中

                //这里使用的是self是外部的this 因为在这里直接使用this,则this的指向有问题

                //并不是直接指向当前的实例,所以这里使用的是外部保存的this

                const component = self.routeMap[self.data.current]

                return h(component)
            }
        })
    }
    initEvent() {

        //这个函数是最后一步要实现的逻辑

        //因为之前编写的逻辑看样子是没问题的但是,无法通过浏览器的前进和后退

        //切换组件,所以呢这个方法是为了实现这个逻辑

        //添加事件监听器,监听popstate事件 第二个参数就是对这个参数需要实现的逻辑

        window.addEventListener('popstate', () => {

            //在这个事件处理函数中我们要实现的逻辑是把当前地址栏的路径取出来

            //仅仅要的是地址栏中/后面的路径并不是要完整路径

            //把这个路径作为路径地址,把路径地址存储到,this.data.current中

            this.data.current = window.location.pathname

        })
    }

}

谢谢观看,如有不足,敬请指教

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值