美文网首页
Vue Router 源码模拟实现

Vue Router 源码模拟实现

作者: 翔子丶 | 来源:发表于2021-02-28 16:12 被阅读0次
    Vue Router实现原理
    • 导入模块 注册插件(Vue.use传入函数时直接调用,传入对象时调用对象install方法)
    • 创建路由对象 new VueRouter 参数是对象
    • 创建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')
    
    实现思路

    VueRouter类

    • options 记录构造函数中传入的对象
    • data 对象 current属性记录当前路由地址 设置为响应式对象,因为路由地址改变对应组件自动更新
    • routeMap 记录路由地址和组件的对应关系
    • Constructor(Options):VueRouter
    • _install(Vue):void 静态方法 实现插件机制
    • init():void 调用下面三个方法
    • initEvent():void 注册pushState事件 用来监听浏览器历史的变化
    • createRouteMap():void 初始化RouteMap 将初始化传入的对象保存到routeMap中
    • initComponents(Vue):void 创建router-view和router-link组件


      image-20210226153507451.png

    完整版 vs 运行时版本

    // 完整版本 需要编译器
    new Vue({
      template: '<div>{{ hi }}</div>'
    })
    
    // 运行时版本 不需要编译器
    new Vue({
      render (h) {
        return h('div', this.hi)
      }
    })
    

    vue-cli 创建的项目默认使用的是运行时版本的 Vue.js,需修改vue.config.js中的配置

    module.exports = {
        runtimeCompiler: true // 设置为 true 后你就可以在 Vue 组件中使用 template 选项
    }
    
    Vue Router history模式实现
    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实例的时候再注入
        // _Vue.prototype.$router = this.$options.router
        _Vue.mixin({
          beforeCreate () {
            // this就是vue实例 判断不是组件时再挂载,组件上没有router
            if (this.$options.router) {
              _Vue.prototype.$router = this.$options.router
            }
          }
        })
      }
    
      constructor (options) {
        this.options = options
        this.routeMap = {}
        // observable 设置为响应式
        this.data = _Vue.observable({
          current: '/' // 记录当前路由地址
        })
        this.init()
      }
    
      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
          },
          render (h) {
            return h(
              'a',
              {
                attrs: {
                  href: this.to
                },
                on: {
                  click: this.clickhander
                }
              },
              [this.$slots.default]
            )
          },
          methods: {
            clickhander (e) {
              history.pushState({}, '', this.to)
              this.$router.data.current = this.to
              e.preventDefault()
            }
          }
          // template:'<a :href='to'><slot></slot></a>'
        })
        const self = this
        Vue.component('router-view', {
          render (h) {
            // self.data.current
            // 当前路由地址对应的组件
            const cm = self.routeMap[self.data.current]
            return h(cm)
          }
        })
      }
    
      initEvent () {
        // 监听历史记录变化popstate
        window.addEventListener('popstate', () => {
          this.data.current = window.location.pathname
        })
      }
    }
    
    Vue Router hash模式实现
    let _vue = null
    export default class VueRouter {
      static install (vue) {
        // 如果插件安装直接返回
        if (VueRouter.install.installed && _vue === vue) return
        VueRouter.install.installed = true
    
        // 记录vue构造函数
        _vue = vue
    
        // 把创建Vue的实例传入的router对象注入到Vue实例,能获取到Vue实例的时候再注入
        _vue.mixin({
          beforeCreate () {
            // 判断vue实例上是否有router对象
            if (this.$options.router) {
              // 把router对象注入到vue实例上
              _vue.prototype.$router = this.$options.router
              // 初始化插件时调用init
              this.$options.router.init()
            }
          }
        })
      }
    
      // 构造函数
      constructor (options) {
        this.options = options
        // 记录路径和组件的对应
        this.routeMap = {}
        // data需要设置为响应式 observable
        this.data = _vue.observable({
          current: '/' // 记录当前路由地址
        })
      }
    
      init () {
        this.initRouteMap()
        this.initComponent(_vue)
        this.initEvent()
      }
    
      // 遍历所有的路由规则,把路由规则解析成键值对的形式存储到routeMap中
      // routes => [{ name: '', path: '', component: }]
      initRouteMap () {
        this.options.routes.forEach((route) => {
          this.routeMap[route.path] = route.component
        })
      }
    
      // 生成 router-link 和 router-view 组件
      initComponent (Vue) {
        Vue.component('RouterLink', {
          props: {
            to: String
          },
          // 需要带编译器版本的 Vue.js
          // template: "<a :href='\"#\" + to'><slot></slot></a>"
          // 使用运行时版本的 Vue.js
          render (h) {
            return h(
              'a',
              {
                attrs: {
                  href: '#' + this.to
                },
                on: {
                  click: this.clickHandler
                }
              },
              [this.$slots.default]
            )
          },
          methods: {
            clickHandler (e) {
              window.location.hash = '#' + this.to
              e.preventDefault()
            }
          }
        })
    
        const self = this
        _vue.component('RouterView', {
          render (h) {
            const component = self.routeMap[self.data.current]
            return h(component)
          }
        })
      }
    
      // 监听历史记录变化
      initEvent () {
        window.addEventListener('hashchange', this.onHashChange.bind(this))
        window.addEventListener('load', this.onHashChange.bind(this))
      }
    
      onHashChange () {
        this.data.current = window.location.hash.substr(1) || '#/'
      }
    }
    

    相关文章

      网友评论

          本文标题:Vue Router 源码模拟实现

          本文链接:https://www.haomeiwen.com/subject/bebgfltx.html