VueRouter 基础功能手写 实现

时间:2021-2-20 作者:admin

文章内容输出来源:拉勾教育大前端高薪训练营

类图

代码实现

实现 install 方法

  1. 判断当前插件是否已经安装
  2. 把Vue构造函数记录到全局变量(后续初始化 router-link router-view 组件还需要Vue构造函数)
  3. 把创建Vue实例时候传入的 router 对象注入到Vue实例上
let _Vue = null;

export default class VueRouter { 
    static install(Vue) {
        // 1、判断当前插件是否已经安装
        if (VueRouter.install.installed) {
            return;
        }
        VueRouter.install.installed = true;  //installed 标记插件已安装
        // 2、Vue构造函数记录到全局变量中 后续还需要vue.compent 处理 vue=link 和 vue-router
        _Vue = Vue;

        // 3、把创建Vue实例时候传入的router对象 注入到Vue实例上 this.$router
        // Vue.use 必须在 Vue实例化之前调用,所以执行 install时候 _Vue实例还没有生成
        _Vue.mixin({  // 我们可以用mixin 混入,在执行 beforeCreate 时候再注入 
            beforeCreate() {
                if (this.$options.router) { //mixin是全局的,保证只初始化一次,可以通过判断Vue实例上是否有router( main.js文件中 new 的 Vue实例),避免后续组件注册重复执行   
                    _Vue.prototype.$router = this.$options.router;
                }
            }
        });
    }
}

初始化构造函数

    constructor(options) {
        this.options = options;
        this.routeMap = {};
        this.data = _Vue.observable({    //使用 Vue.observable 生成可响应对象 data
            current: "/"
        });
    }

创建 routeMap

    createRouteMap() {
        // 遍历所有的路由规则,把路由规则解析成键值对的形式 存储到routeMap
        this.options.routes.forEach(route => {
            this.routeMap[route.path] = route.component;
        });
    }

创建和注册全局组件 router-link 和 router-view

      initComponents(Vue) {
        //传入Vue构造函数,减少方法和外部的依赖
        Vue.component("router-link", {
            props: {
                to: String
            },
            // template: "<a :href='to'><slot></slot></a>"  // template 需要使用带编译器版本Vue
            render(h) {
                return h('a', {
                    attrs: {
                        href: this.to
                    },
                    on: {
                        click: this.clickHandler
                    },
                }, [this.$slots.default])
            },
            methods: {
                clickHandler(e) {
                    history.pushState({}, '', this.to) // 向当前浏览器会话的历史堆栈中添加一个状态(state)
                    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) // router-view占位替换为对应的组件
            }
        })
    }

监听浏览器前进后退操作

 initEvent() {
        window.addEventListener('popstate', () => {
            this.data.current = window.location.pathname  // current 是响应式的,router-view更新
        })
    }

完整代码代码

最后在注册时候,调用初始化方法,完成基本 VueRouter history模式模拟

let _Vue = null;

export default class VueRouter {   
    static install(Vue) {
        if (VueRouter.install.installed) {
            return;
        }
        VueRouter.install.installed = true;
        _Vue = Vue;
        _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() {
        this.options.routes.forEach(route => {
            this.routeMap[route.path] = route.component;
        });
    }
    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) {
                    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]
                console.log(component)
                return h(component)
            }
        })
    }
    initEvent() {
        window.addEventListener('popstate', () => {
            this.data.current = window.location.pathname
        })
    }
}

总结

通过模拟 VueRouter history 模式的功能,了解 Vue 插件基础和 VueRouter 功能,通过个人转述,加深理解和记忆,如果有什么地方表达错误或不当,希望能帮忙指出

最后话语

​ 小公司工作几年了,时长能感觉到自己知识的欠缺,很多技术都了解一些,但是用过后逐渐模糊,新技术也在不断产生和变革,不断地写业务,和东拼西凑的学习知识,深感自己技术广度和深度都缺乏,进步缓慢。

​ 今年听到大佬举了个例子,大概是这样:“你埋头面对高考学习,但是你连要考什么都不知道,结果可想而知”,才醒悟过来,缺乏当下主流技术、热点和知识体系的认知和实践,学习当然是事倍功半。

​ 经过对比和试听,最后选择了拉勾教育,拉勾教育的课程很详细和全面,视频按照知识点切分,每个视频都不会半个小时一个小时那么长,以往看视频容易瞌睡,拉勾的视频设置合理,短的只有几分钟,方便结合实践和利用碎片化时间,非常棒,最后期望拉勾教育越办越好,也期待自己好好补充知识体系,追赶上技术主流

声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。