目录
  • 回顾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')
    

    注意,上述版本为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
            }
          },
        })
      }
    }

    构造函数

    constructor(options) {
      this.options = options
      this.routeMap = {}
      this.data = _Vue.observable({
        current: '/'
      })
    }
    

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

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

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

    initComponent(Vue) {
      Vue.component('router-link', {
        props: {
          to: String
        },
        // template: `<a :href="https://www.freexyz.cn/dev/to" rel="external nofollow"  rel="external nofollow" ><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 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
        })
    }
    

    完整代码

    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: `<a :href="https://www.freexyz.cn/dev/to" rel="external nofollow"  rel="external nofollow" ><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 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
        })
      }
    }
    声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。