您现在的位置是:首页 >技术交流 >vue的路由的原理(自己封装一个vue-router插件)网站首页技术交流

vue的路由的原理(自己封装一个vue-router插件)

米奇妙妙wuu 2024-06-17 11:28:33
简介vue的路由的原理(自己封装一个vue-router插件)

前言:

Vue Router 是 Vue.js 官方的路由管理器,它和 Vue.js 的核心深度集成,可以非常方便地实现单页应用程序的路由功能

路由实例化:

Vue Router 的核心是一个 Vue 插件,通过调用 Vue.use(VueRouter) 方法来实例化一个路由对象。在 VueRouter 的构造函数中,会初始化一些基本属性和方法,如 options、history、current、match 等。

 constructor(options) {
    this.$options = options;
    Vue.util.defineReactive(this, 'matched', [])
    const url = location.hash.slice(1) || '/'
    // 定义一个响应式的current属性
    Vue.util.defineReactive(this, 'current', url);

    this.init();
  }
   // 初始化函数
  init() {
    this.match();
    this.bindEvent();
  }

路由匹配:

Vue Router 的路由匹配是通过 match 方法实现的。match 方法接收一个 route 对象,返回一个匹配的路由记录。在 match 方法中,会遍历路由表,根据路由表中的 path 和 route.path 进行匹配,如果匹配成功,则返回对应的路由记录。
bindEvent方法绑定hashchange事件,监听hash值的变化

match(routes) {
    routes = routes || this.$options.routes;
    
    routes.forEach(route => {
      // 处理路由完全匹配(根路径)
      if(route.path === '/' && this.current.includes(route.path)) {
        this.matched.push(route)
  
      }
      // /course/vue
      if(route.path !== '/' && this.current.includes(route.path)) {
        this.matched.push(route);
        if(route.children) {
          this.match(route.children)
        }
      }
    })
  }
 // 绑定hashchange事件,监听hash的变化
  bindEvent() {
    // 监听hash的变化
    addEventListener('hashchange', () => {
      // 当hash值改变时,将最新的hash值赋值给current
      this.current = location.hash.slice(1) || '/'
      // 清空路由数组
      this.matched = [];
      this.match();
    })
  }

路由跳转:

Vue Router 的路由跳转是通过 push、replace、go、back 等方法实现的。在这些方法中,会调用 history 对象的 pushState、replaceState、go、back 等方法,实现浏览器的前进后退和地址栏的变化。

路由钩子:

Vue Router 的路由钩子是通过 beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave 等方法实现的。在这些方法中,可以进行路由拦截、权限控制、数据预取等操作。

 Vue.mixin({
    beforeCreate() {
      if(this.$options.router) {
      }
    }
  })

插件调用install方法

VRouter.install = function(_Vue) {
  Vue = _Vue;

  Vue.mixin({
    beforeCreate() {
      if(this.$options.router) {
        // 这样做就可以通过组件的实例访问到router对象
        Vue.prototype.$router = this.$options.router;
      }
    }
  })

  // 注册router-link和router-view两个全局组件
  Vue.component('router-link', RouterLink)
  Vue.component('router-view', RouterView)
}

封装RouterView

  • 对被routerView渲染的组件进行标记为true
  • 在app组件中被routerView渲染机组件层级初始值标记为0
  • 找到当前组件的父组件
  • 对父组件进行循环,知道找到app组件,再循环中判断当前的子组件是否为routerView组件,如果是,层级**+1**
  • 定义存储组件的变量,找到对应组件赋值
export default {
  render(h) {
    // 对当前的routerView组件进行标记(即被routerView渲染的组件)
    this.$vnode.data.routerView = true;
    // 路由嵌套层级计算(在App组件中被routerView渲染的组件层级为0)
    let depath = 0;
    // 找到当前组件的父组件(在App组件中被routerView渲染的组件的父组件就是根组件App)
    let parent = this.$parent;
    
    // 循环父组件,一直找到App组件退出循环
    while(parent) {
      // 一直往下找子组件
      const vnodeData = parent.$vnode && parent.$vnode.data;
      if(vnodeData) {
        // 判断当前子组件是否是routerView组件,如果是让其层级+1
        if(vnodeData.routerView) {
          depath++;
        }
      }
      parent = parent.$parent;
    }

    // 定义存储组件的变量
    let componet = null;

    // 找到对应组件赋值
    const route = this.$router.matched[depath];
    if(route) {
      componet = route.component;
    }

    return h(componet)
  }
}

封装RuoterLink

export default {
  props: {
    to: {
      type: String | Object,
      required: true
    }
  },
  render(h) {
    return h('a', { attrs: { 'href': '#' + this.to } }, this.$slots.default)
  }
}

详细步骤

main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
  router
}).$mount('#app')

src outerindex.js

import Vue from 'vue';
import VueRouter from '../plugins/router'

Vue.use(VueRouter)

const router = new VueRouter({
  routes: [
    {
      path: '/home',
      component: () => import('../components/Home')
    },
    {
      path: '/cate',
      component: () => import('../components/Cate')
    },
    {
      path: '/course',
      component: () => import('../components/Course'),
      children: [
        {
          path: '/course/vue',
          component: () => import('../components/course/VueCourse')
        },
        {
          path: '/course/react',
          component: () => import('../components/course/ReactCourse')
        }
      ]
    }
  ]
})

export default router;

srcplugins outer.js

import RouterLink from './components/RouterLink'
import RouterView from './components/RouterView'

let Vue;
class VRouter {
  constructor(options) {
    this.$options = options;
    Vue.util.defineReactive(this, 'matched', [])
    const url = location.hash.slice(1) || '/'
    // 定义一个响应式的current属性
    Vue.util.defineReactive(this, 'current', url);

    this.init();
  }
  // 初始化函数
  init() {
    this.match();
    this.bindEvent();
  }
  match(routes) {
    routes = routes || this.$options.routes;
    
    routes.forEach(route => {
      // 处理路由完全匹配(根路径)
      if(route.path === '/' && this.current.includes(route.path)) {
        this.matched.push(route)
  
      }
      // /course/vue
      if(route.path !== '/' && this.current.includes(route.path)) {
        this.matched.push(route);
        if(route.children) {
          this.match(route.children)
        }
      }
    })

    // console.log(this.matched);
  }
  // 绑定hashchange事件,监听hash的变化
  bindEvent() {
    // 监听hash的变化
    addEventListener('hashchange', () => {
      // 当hash值改变时,将最新的hash值赋值给current
      this.current = location.hash.slice(1) || '/'
      // 清空路由数组
      this.matched = [];
      this.match();
    })
  }
}

VRouter.install = function(_Vue) {
  Vue = _Vue;

  Vue.mixin({
    beforeCreate() {
      if(this.$options.router) {
        // 这样做就可以通过组件的实例访问到router对象
        Vue.prototype.$router = this.$options.router;
      }
    }
  })

  // 注册router-link和router-view两个全局组件
  Vue.component('router-link', RouterLink)
  Vue.component('router-view', RouterView)
}

export default VRouter;

srcpluginscomponentsRouterView.js

export default {
  render(h) {
    // 对当前的routerView组件进行标记(即被routerView渲染的组件)
    this.$vnode.data.routerView = true;
    // 路由嵌套层级计算(在App组件中被routerView渲染的组件层级为0)
    let depath = 0;
    // 找到当前组件的父组件(在App组件中被routerView渲染的组件的父组件就是根组件App)
    let parent = this.$parent;
    
    // 循环父组件,一直找到App组件退出循环
    while(parent) {
      // 一直往下找子组件
      const vnodeData = parent.$vnode && parent.$vnode.data;
      if(vnodeData) {
        // 判断当前子组件是否是routerView组件,如果是让其层级+1
        if(vnodeData.routerView) {
          depath++;
        }
      }
      parent = parent.$parent;
    }

    // 定义存储组件的变量
    let componet = null;

    // 找到对应组件赋值
    const route = this.$router.matched[depath];
    if(route) {
      componet = route.component;
    }

    return h(componet)
  }
}

srcpluginscomponentsRouterLink.js

export default {
  props: {
    to: {
      type: String | Object,
      required: true
    }
  },
  render(h) {
    return h('a', { attrs: { 'href': '#' + this.to } }, this.$slots.default)
  }
}
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。