手写 Vue Router,揭秘路由背后的魔法!🔮

手写 Vue Router,揭秘路由背后的魔法!🔮

前言:从使用者到创造者的转变

大家好!在日常的 Vue 开发中,我们经常使用 Vue Router 来管理路由。但你是否曾好奇过,这个看似神奇的 router 到底是如何工作的?今天,就让我们一起揭开 Vue Router 的神秘面纱,从零开始手写一个简易版的路由器!

回顾:Vue Router 的基本使用

在开始造轮子之前,让我们先回忆一下 Vue Router 的标准用法:

1. 全局注册路由

js 复制代码
// main.js
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router'

createApp(App)
  .use(router)  // 这一步很关键!
  .mount('#app')

🤔 思考时刻 :这个 .use(router) 到底做了什么魔法?

2. 创建路由实例

js 复制代码
// router/index.js
import { createRouter, createWebHashHistory } from 'vue-router'
import Home from '../pages/Home.vue'
import About from '../pages/About.vue'

const routes = [
  {
    path: '/',
    name: 'home',
    component: Home
  },
  {
    path: '/about',
    name: 'about',
    component: About
  }
]

const router = createRouter({
  history: createWebHashHistory(), // 创建 hash 模式
  routes
})

export default router

3. 在组件中使用路由

vue 复制代码
<!-- App.vue -->
<template>
  <div>
    <div>
      <router-link to="/">Home</router-link> |
      <router-link to="/about">About</router-link>
    </div>
    <router-view/>
  </div>
</template>

🎯 观察发现<router-link><router-view> 这两个组件就像是凭空出现的魔法道具!

揭秘时刻:Vue Router 的核心构成

揭秘 app.use(router) 的魔法!🔮

当我们写下这行看似简单的代码时:

js 复制代码
createApp(App).use(router).mount('#app')

实际上发生了一系列精妙的"魔法"。让我为你一步步揭开这个神秘面纱!

1. app.use() 是什么?

app.use() 是 Vue 3 的插件系统的核心方法。它的作用是:

安装一个插件,扩展 Vue 应用的功能

就像给你的应用安装"扩展程序"一样!📦

2. app.use() 的工作原理

当调用 app.use(router) 时,Vue 内部会执行以下检查:

js 复制代码
// 伪代码:Vue 内部的 use 方法逻辑
use(plugin, options) {
  if (plugin && typeof plugin.install === 'function') {
    // 情况1:插件有 install 方法
    plugin.install(app, options)
  } else if (typeof plugin === 'function') {
    // 情况2:插件本身是个函数
    plugin(app, options)
  } else {
    console.warn('无效的插件')
  }
}

在我们的路由场景中,router 对象有一个 install 方法,所以会走第一种情况!

3. 路由的 install 方法做了什么?

让我们回顾手写路由中的 install 方法:

js 复制代码
class Router {
  // ... 其他代码
  
  install(app) {
    // 魔法1:建立全局"秘密通道"
    app.provide(ROUTER_KEY, this)
    
    // 魔法2:注册全局组件
    app.component('router-link', RouterLink)
    app.component('router-view', RouterView)
    
    console.log('🎉 路由安装成功!', app)
  }
}
魔法1:app.provide(ROUTER_KEY, this)

这行代码创建了一个依赖注入系统

js 复制代码
// 提供者(在路由安装时)
app.provide(ROUTER_KEY, this) // this 就是路由实例

// 消费者(在任何组件中)
const router = inject(ROUTER_KEY) // 获取路由实例

想象一下,这就像在应用中建立了一条"秘密地铁线路"🚇:

  • 起点站app.provide() 把路由实例放进"地铁"
  • 任意站点 :任何组件都可以通过 inject() 上车,获取路由实例

这样就不需要通过 props 一层层传递了!避免了"props 钻地洞"的麻烦。

魔法2:注册全局组件
js 复制代码
app.component('router-link', RouterLink)
app.component('router-view', RouterView)

这相当于向 Vue 声明:

"亲爱的 Vue,从现在开始,无论在哪个组件中看到 <router-link><router-view>,请使用我提供的这两个组件来渲染它们!"

就像给 Vue 的"词汇表"里添加了两个新单词!📚

4. 为什么需要这个魔法?

没有 app.use(router) 的话:

  1. 组件不认识 :Vue 看到 <router-link> 会一脸懵逼🤔
  2. 实例无法共享:每个组件都要手动接收路由实例
  3. 代码重复:在每个组件都要导入和使用路由

有了 app.use(router)

  1. 开箱即用:全局组件随处可用 🎁
  2. 轻松访问:任何组件都能获取路由实例
  3. 维护简单:路由逻辑集中管理
总结

app.use(router) 的魔法其实就是:

一个标准化的插件安装协议,让路由能够:

  1. 注册全局组件 - 让 Vue 认识 <router-link><router-view>
  2. 建立依赖注入 - 让所有组件都能轻松访问路由实例
  3. 执行初始化逻辑 - 为路由功能做准备

这就像给 Vue 应用安装了一个"路由操作系统",让整个应用具备了页面导航和组件切换的能力!💫

现在,让我们思考一个问题:如果要手写 Vue Router,我们需要实现什么?

从 Vue Router 的 API 文档中,我们可以看到路由实例包含了很多方法:

但今天,我们重点关注三个核心部分:

  • createRouter - 创建路由实例
  • createWebHashHistory - 创建 hash 模式
  • 路由实例的 install 方法

手写 Vue Router 实战 🛠️

让我们开始动手实现自己的路由吧!

第一步:创建路由核心文件

js 复制代码
// grouter/index.js
import { ref, inject } from 'vue'
import RouterLink from './RouterLink.vue'
import RouterView from './RouterView.vue'

const ROUTER_KEY = '__router__' // 依赖注入的密钥

// 创建路由实例
function createRouter(options) {
  return new Router(options)
}

// 创建 hash 模式(简化版)
function createWebHashHistory() {
  // 这里先留空,后续完善
  return {}
}

// 在组件中获取路由实例的 hook
function useRouter() {
  return inject(ROUTER_KEY)
}

// 路由类 - 核心大脑
class Router {
  constructor(options) {
    this.routes = options.routes // 存储路由配置
    // 这里还可以添加更多功能,比如当前路由状态、导航守卫等
  }
  
  // 必须的 install 方法 - 这是 app.use(router) 时调用的!
  install(app) {
    // 1. 提供路由实例给所有组件
    app.provide(ROUTER_KEY, this)
    
    // 2. 注册全局组件
    app.component('router-link', RouterLink)
    app.component('router-view', RouterView)
    
    console.log('🎉 路由安装成功!', app)
  }
}

export { createRouter, createWebHashHistory, useRouter }
vue 复制代码
<!-- grouter/RouterLink.vue -->
<template>
  <a :href="'#' + props.to">
    <!-- :href="'#'+to" 表示动态的 hash 链接 -->
    <slot />
    <!-- slot 插槽:让使用者可以自定义链接内容 -->
  </a>
</template>

<script setup>
import { defineProps } from 'vue'

// 定义组件 props
let props = defineProps({
  to: {
    type: String,
    default: '/'
  }
})
</script>

<style scoped>
/* 可以在这里添加链接样式 */
</style>

💡 知识点<slot /> 就像是一个占位符,允许使用者插入自定义内容,比如:

vue 复制代码
<router-link to="/about">
  🚀 关于我们  <!-- 这个内容会替换 <slot /> -->
</router-link>

第三步:实现 RouterView 组件

vue 复制代码
<!-- grouter/RouterView.vue -->
<template>
  <div>
    <!-- component 是 Vue 的动态组件 -->
    <!-- :is="component" 根据计算属性动态决定渲染哪个组件 -->
    <component :is="component" />
  </div>
</template>

<script setup>
import { computed } from 'vue'
import { useRouter } from '../grouter/index'

// 获取路由实例
let router = useRouter()

// 计算属性:根据当前 hash 找到对应的组件
const component = computed(() => {
  // window.location.hash 获取当前 hash(去掉 # 号)
  const currentHash = window.location.hash.slice(1) || '/'
  
  // 在路由配置中查找匹配的组件
  const route = router.routes.find(item => item.path === currentHash)
  
  return route ? route.component : null
})
</script>

<style scoped>
/* 视图容器样式 */
</style>

🎯 核心逻辑RouterView 就像是一个智能的组件放映机,根据 URL 的变化自动切换显示的组件!

深入理解关键概念 🧠

1. app.use() 的魔法

当我们调用 app.use(router) 时,Vue 会:

  • 检查 router 是否有 install 方法
  • 如果有,就调用 router.install(app, ...options)
  • 这就是插件机制的奥秘!

2. 依赖注入:provideinject

js 复制代码
// 在路由安装时提供实例
app.provide(ROUTER_KEY, this)

// 在任意组件中获取路由实例
const router = inject(ROUTER_KEY)

这就像建立了一条"秘密通道",让所有组件都能访问到路由实例,而不需要通过 props 层层传递!

3. 全局组件注册

js 复制代码
app.component('router-link', RouterLink)
app.component('router-view', RouterView)

这相当于告诉 Vue:"嘿,以后在任何地方看到 <router-link><router-view>,就用我提供的这些组件来渲染!"

使用我们手写的路由

现在,我们可以像使用官方 Vue Router 一样使用我们自己的路由了!

js 复制代码
// router/index.js - 改用我们的手写路由
import { createRouter, createWebHashHistory } from './grouter/index'
// ... 其他代码保持不变

// main.js - 使用方式完全一样!
import router from './router'
app.use(router)

效果展示 ✨

总结与展望 🚀

通过这个简易的路由实现,我们学到了:

  1. 插件机制 :Vue 的 app.use() 如何工作
  2. 依赖注入 :如何使用 provide/inject 共享状态
  3. 全局组件:如何注册全局可用的组件
  4. 动态组件 :Vue 的 <component :is> 的妙用
  5. 路由原理:hash 路由的基本工作原理

当然,这只是一个基础版本。完整的 Vue Router 还包括:

  • 历史记录管理(HTML5 History API)
  • 路由参数解析
  • 导航守卫
  • 路由嵌套
  • 懒加载等高级功能

但最重要的是,我们现在理解了路由的核心原理!下次使用 Vue Router 时,你会更加清楚背后发生的事情。

希望这篇文章对你有所帮助!如果有任何问题,欢迎在评论区讨论~ 💬

编程的乐趣不仅在于使用工具,更在于理解工具背后的原理! 🎉


PS:想要挑战自己的话,可以尝试实现路由参数解析或导航守卫功能哦!

相关推荐
菜鸟‍4 小时前
【前端学习】仿Deepseek官网AI聊天网站React
前端·学习·react.js
小光学长4 小时前
基于Vue的保护动物信息管理系统r7zl6b88 (程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
前端·数据库·vue.js
huangql5205 小时前
截图功能技术详解:从原理到实现的完整指南
前端·html5
长空任鸟飞_阿康5 小时前
Node.js 核心模块详解:fs 模块原理与应用
前端·人工智能·ai·node.js
这儿有一堆花5 小时前
网站链接重定向原理
前端
麦麦大数据5 小时前
F029 vue游戏推荐大数据可视化系统vue+flask+mysql|steam游戏平台可视化
vue.js·游戏·信息可视化·flask·推荐算法·游戏推荐
cecyci5 小时前
如何实现AI聊天机器人的打字机效果?
前端·javascript
IT_陈寒5 小时前
Vite 5个隐藏技巧让你的项目构建速度提升50%,第3个太香了!
前端·人工智能·后端
詩句☾⋆᭄南笙6 小时前
HTML的盒子模型
前端·html·盒子模型