vue-router实现

vue-router在vue开发中是我们最常接触到的,但对于其实现还是比较陌生的
vue-router最常见的用法:
首先在main.js中引入

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

Vue.config.productionTip = false

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

接着定义路由文件
router/index.js

import Vue from 'vue'
// import VueRouter from 'vue-router'
import VueRouter from '../vue-router'
import Home from '../views/Home.vue'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
  }
]

const router = new VueRouter({
  routes
})

export default router

最后,在App.vue中使用

<template>
  <div id="app">
    <div id="nav">
      <router-link to="/">Home</router-link> |
      <router-link to="/about">About</router-link>
    </div>
    <router-view/>
  </div>
</template>

分析一下vue-router
首先vue-router是一个vue插件,所以需要实现插件方法install
接着我们需要记录vue的路由配置,最好能形成一个map,键为路由的路径,值为路径对应的component
然后我们需要实现路由组件router-link、router-view
最后我们要让组件能响应浏览器的前进后退按钮

先来完成第一步

let Vue;
export default class VueRouter{
  static install(_Vue) {
    if(VueRouter.installed) {
      return
    }
    VueRouter.installed = true;
    
    Vue = _Vue
    
    Vue.mixin({
      beforeCreate() {
        if(this.$options.router) {
          Vue.prototype.$router = this.$options.router
          //执行初始化函数
          this.$options.router.init()
        }
      }
    })
  }
  init() {}
}
//解释一下
//变量Vue记录vue的构造函数,让我们能够用mixin、component等全局函数
//install方法会传入vue的构造函数
//用一个installed变量记录是否已经挂载了vue-router插件
//使用mixin给所有vue实例都挂在一个beforeCreate方法,但只有具有router属性的实例,也就是main函数中的实例才具有router,才能为vue原型添加router,其他组件实例找到的是构造函数原型上的实例
new Vue({
  router,
  render: h => h(App)
}).$mount('#app')

完成第二步,

export default class VueRouter{
  static install(_Vue) {
    //some code
  }
  constructor(options) {
    this.options = options; //记录路由配置信息,也就是router/index.js中的路由配置
    this.routerMap = {}; //路由map
    this.data = Vue.observable({ //这是vue提供的api,能让一个对象变为响应式
      current: '/' //记录当前路径
    })
  }
  initRouteMap() { //初始化routerMap
    this.options.forEach(item => {
       this.routerMap[item.path] = item.component
    })
  }
  init() {
    this.initRouteMap()
  }
}

接下来实现router-link,router-view

export default class VueRouter{
  static install(_Vue) {
    //some codes
  }
  constructor(options) {
    // some codes
  }
  initRouteMap() { //初始化routerMap
    //some codes
  }
  initComponent(Vue) {
    //  router-link实现
    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方法
          history.pushState({}, "", this.to);
          this.$router.data.current = this.to;//记录当前路径
          e.preventDefault();//阻止a标签默认事件
        }
      },
    })
    //router-view实现
    const self = this;
    Vue.component('router-view', {
      render(h) {
        const component = self.routeMap[self.data.current]//获取当前路径对应的组件
        return h(component)
      },
    })
  }
  init() {
    this.initRouteMap()
    this.initComponent(Vue)
  }
}
//注意要阻止默认a标签事件,因为a标签默认事件会跳转刷新网页

完成最后一步

export default class VueRouter{
  static install(_Vue) {
    //some codes
  }
  constructor(options) {
    // some codes
  }
  initRouteMap() { //初始化routerMap
    //some codes
  }
  initComponent(Vue) {
    //some codes
  }
  //初始化事件
  initEvent() {
    window.addEventListener("popstate", () => { //history提供了popstate用来监听路由变化
      this.data.current = window.location.pathname
    })
  }

  //初始化
  init() {
    this.initRouteMap();
    this.initComponent(Vue);
    this.initEvent()
  }
}
//我们已经将this.data转变为了一个响应式对象,当路径发生改变时就会触发dom重新渲染

完整代码

let Vue;

export default class VueRouter {
  static install(_Vue) {
    if(VueRouter.installed) { //1.是否已安装VueRouter
      return
    }
    VueRouter.install.installed = true;

    Vue = _Vue //2.缓存Vue构造函数

    Vue.mixin({ //3.将VueRouter混入到所有Vue实例中
      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: '/' //该属性记录当前路由地址
    })
  }

  //初始化路由配置
  initRouteMap() {
    this.options.routes.forEach(item => {
      this.routeMap[item.path] = item.component;
    })
  }

  //初始化路由组件
  initComponent(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]
        return h(component)
      },
    })
  }

  //初始化事件
  initEvent() {
    window.addEventListener("popstate", () => {
      this.data.current = window.location.pathname
    })
  }

  //初始化
  init() {
    this.initRouteMap();
    this.initComponent(Vue);
    this.initEvent()
  }
}
posted @   我喝牛奶不舔盖  阅读(21)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
I hear and I forget. I see and I remember. I do and I understand
点击右上角即可分享
微信分享提示