• Vue源码探秘(一)——Vue-Router原理实现


    在看这篇文章的几点要求:需要你先知道Vue-Router是个什么东西,用来解决什么问题,以及它的基本使用。如果你还不懂的话,建议上官网了解下Vue-Router的基本使用后再回来看这篇文章。Vue Router官网

    前置知识:插件、slot插槽、mixins混入、render函数、运行时和完整版的Vue


    回顾Vue Router的核心代码

    // 注册插件
    // Vue.use() 内部调用传入对象的 install 方法
    Vue.use(VueRouter)
    // 创建路由对象
    const router = new VueRouter({
      routes: [
        { name: 'home', path: '/', component: homeComponent }
      ]
    })
    // 创建 Vue 实例,注册 router 对象
    new Vue({
      router,
      render: h => h(App)
    }).$mount('#app')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    注意,上述版本为Vue Router 3.x,用法与最新版的有所不同,但理解了其核心设计思路后,再去手写Vue4也是得心应手。

    Vue功能的强大就在于它的插件机制,use可以接受两种参数——函数和对象。如果是函数的话,则会直接调用这个函数,如果是对象的话,则会调用对象的install方法。


    代码实现

    创建Vue-Router插件

    let _Vue = null
    
    export default class VueRouter {
      static install(Vue) {
        // 1. 判断插件是否已经安装,如果是的话,直接返回
        if (Vue.install.installed) return
        Vue.install.installed = true
    
        // 2.将Vue的构造函数记录在全局
        _Vue = Vue
    
        // 3.把创建Vue的实例传入的router对象注入到Vue实例
        _Vue.mixin({
          beforeCreate() {
            // 判断是否是实例,如果是实例就不用添加
            if (this.$options.router) {
              _Vue.prototype.$router = this.$options.router
            }
          },
        })
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    构造函数

    constructor(options) {
      this.options = options
      this.routeMap = {}
      this.data = _Vue.observable({
        current: '/'
      })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    将options中的路由规则存放到routeMap中

    // 注意,下面的代码只考虑了path和component对应,name同理,也可加上路由元信息,或者遍历加上children,但为便捷及个人水平考虑,并未完整实现
    createRouteMap() {
      // 遍历所有的路由规则,把路由股则解析成键值对的形式,存储到routeMap中
      this.options.routes.forEach(route => {
        this.routeMap[route.path] = route.component
      })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    初始化route-link和router-view两个组件

    initComponent(Vue) {
      Vue.component('router-link', {
        props: {
          to: String
        },
        // template: ``
        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]
          return h(component)
        }
      })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    浏览器回退、前进时页面同时改变

    initEvent(){
        window.addEventListener("popstate",()=>{
            this.data.current = window.location.pathname
        })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    完整代码

    let _Vue = null
    
    export default class VueRouter {
      constructor(options) {
        this.options = options
        this.routeMap = {}
        this.data = _Vue.observable({
          current: '/'
        })
        this.init()
      }
    
      static install(Vue) {
        // 1. 判断插件是否已经安装,如果是的话,直接返回
        if (Vue.install.installed) return
        Vue.install.installed = true
    
        // 2.将Vue的构造函数记录在全局
        _Vue = Vue
    
        // 3.把创建Vue的实例传入的router对象注入到Vue实例
        _Vue.mixin({
          beforeCreate() {
            // 判断是否是实例,如果是实例就不用添加
            if (this.$options.router) {
              _Vue.prototype.$router = this.$options.router
            }
          },
        })
      }
    
      init() {
        this.createRouteMap()
        this.initComponent(_Vue)
        this.initEvent()
      }
    
      createRouteMap() {
        // 遍历所有的路由规则,把路由股则解析成键值对的形式,存储到routeMap中
        this.options.routes.forEach(route => {
          this.routeMap[route.path] = route.component
        })
      }
    
      initComponent(Vue) {
        Vue.component('router-link', {
          props: {
            to: String
          },
          // template: ``
          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]
            return h(component)
          }
        })
      }
    
      initEvent() {
        window.addEventListener('popstate', () => {
          this.data.current = window.location.pathname
        })
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
  • 相关阅读:
    zookeeper介绍
    PC-lint静态检测工具集成到SourceInsight配置步骤
    大一新生HTML期末作业 学生个人网页设计作业 HTML5响应式个人简历网站模板 web前端网页制作课作业
    (附源码)ssm教师工作量核算统计系统 毕业设计 162307
    【Java 设计模式】创建者模式 之抽象工厂模式
    Git使用
    .Net6 Api Swagger配置
    解决idea运行maven项目报错Unresolved plugin ‘org.apache.maven.pluginsxxxx
    【c++】向远程服务器传输文件实现
    nodejs+vue+elementui房屋租赁网站系统 python协同过滤推荐系统
  • 原文地址:https://blog.csdn.net/weixin_49172439/article/details/126689781