手搓迷你Vue Router:当“路由”遇见“代码料理”
在现代Web开发中,尤其是构建单页面应用程序(SPA),Vue Router 成为了连接视图和路由逻辑的关键桥梁。本文将通过手搓一个简易版的Vue Router,帮助我们理解其核心原理和工作机制。我们将从创建路由器实例、定义路由历史模式、实现路由组件和钩子,以及使用自定义路由组件几个关键方面展开。
一、创建路由器实例
首先,我们定义一个createRouter
函数,它接受路由选项并返回一个Router
类的实例。Router
类负责处理路由状态和事件绑定。
// 单例的责任
export const createRouter = (options) => {
return new Router(options)
}
const ROUTER_KEY = '__router__'
export const useRouter = () => {
return inject(ROUTER_KEY)
}
class Router {
constructor(options) {
this.history = options.history
this.routes = options.routes
this.current = ref(this.history.url)
this.history.bindEvents(() => {
// console.log('//////////')
this.current.value = window.location.hash.slice(1)
})
}
// use 调用 插件install
install(app) {
// 全局声明有一个router 全局使用的对象
app.provide(ROUTER_KEY,this)
console.log('准备与vue 对接', app)
app.component('router-link', RouterLink)
app.component('router-view', RouterView)
}
}
这段代码定义了一个简单的单例模式的路由系统,用于Vue应用中。下面是关键点的简要解析:
-
createRouter
函数:- 这是一个工厂函数,接受配置选项并返回一个新的
Router
实例。 - 它确保了在整个应用程序中,路由管理器只有一个实例。
- 这是一个工厂函数,接受配置选项并返回一个新的
-
useRouter
函数:- 这个函数用于在组件中获取路由实例。
- 它使用 Vue 的
inject
方法来访问在父组件或应用根注入的路由器实例。
-
Router
类:constructor
: 构造函数接收一个配置对象,包含历史记录(history)和路由规则(routes)。this.history
和this.routes
分别存储了历史记录和路由规则。this.current
是一个响应式引用(ref),存储当前URL路径。bindEvents
方法绑定事件监听器,当URL变化时更新current
的值。
-
install
方法:- 当这个方法被调用时,它将
Router
实例注入到 Vue 应用中,使得任何组件都可以通过useRouter
访问它。 - 它还注册了两个全局组件:
router-link
和router-view
,用于导航和渲染匹配的组件。
- 当这个方法被调用时,它将
-
全局组件:
RouterLink
组件通常用于创建链接,可以导航至不同的路由。RouterView
组件则用于渲染与当前活动路由相匹配的组件。
这个路由系统遵循了单例设计模式,并与 Vue 的响应式系统和组件系统集成,以实现动态路由和视图切换。
二、定义路由历史模式
接下来,我们实现两种路由历史模式:哈希模式和历史模式。此处仅展示哈希模式的实现:
export const createWebHashHistory = () => {
function bindEvents(fn) {
window.addEventListener('hashchange', fn)
}
// history 对象
return {
url: window.location.hash.slice(1) || '/',
bindEvents
}
}
这段代码定义了一个名为 createWebHashHistory
的函数,用于创建基于浏览器 hash
的历史记录对象,这是前端单页应用中常见的路由管理方式之一。以下是关键点解析:
-
bindEvents
函数:- 这是一个内部函数,用于绑定
hashchange
事件到指定的回调函数fn
。 - 当浏览器地址栏的 hash 发生变化时,这个事件会被触发,从而允许你更新应用状态或重新渲染页面。
- 这是一个内部函数,用于绑定
-
返回的对象:
-
返回一个对象,该对象有两个属性:
url
: 初始化为当前浏览器 URL 中的 hash 值(如果存在的话),如果没有 hash,则默认为'/'
。bindEvents
: 这是上述定义的bindEvents
函数,用于外部代码监听hashchange
事件。
-
总结来说,createWebHashHistory
创建了一个封装了浏览器 hash 变化监听功能的对象,这个对象可以被路由系统用来监控 URL hash 的变化,从而触发相应的路由逻辑。
三、实现路由组件和钩子
1.为了使路由组件能在Vue应用中使用,我们需要定义RouterLink
和RouterView
组件。下面展示了RouterLink
的基本实现:
<template>
<a :href="'#' + props.to">
<slot />
</a>
</template>
<script setup>
import { defineProps} from 'vue'
const props = defineProps({
to: {
type: String,
required: true
}
})
</script>
这段代码展示了如何在 Vue 3 中创建一个 <router-link>
组件的简化版本。下面是关键点的简要解析:
-
模板 (
template
) :- 使用
<a>
标签作为基础元素来构建链接。 - 动态绑定
href
属性,值为'#'
加上props.to
的值,这会改变浏览器地址栏的 hash 部分而不触发页面刷新,适合基于 hash 的路由系统。
- 使用
-
脚本 (
script setup
) :- 使用 Vue 3 的 Composition API 和
defineProps
函数来定义组件的属性。 props
对象接收一个to
属性,其类型为字符串且是必需的,表示链接的目标路由路径。
- 使用 Vue 3 的 Composition API 和
小结:
- 这个组件接收一个
to
属性,用于指定链接的路由目标。 - 它使用
<a>
标签并通过修改href
属性值来创建一个指向特定 hash 的链接,这通常用于单页应用的客户端路由。 - 组件内部使用了 Vue 3 的 Composition API,使代码更简洁、易于理解和维护。
2.RouterView
组件用于动态渲染当前路由匹配的组件:
<template>
<component :is="component"></component>
</template>
<script setup>
import { useRouter } from './index.js'
import { computed } from 'vue'
const router = useRouter();
console.log(router)
const component = computed(() => {
const route = router.routes.find(
(route) => route.path == router.current.value
)
console.log(route,'??????//////')
return route? route.component:null
})
</script>
这段代码展示了一个简单的 Vue 3 组件,它的作用是在当前活动的路由路径下渲染相应的组件。下面是关键点的解析:
-
模板 (
template
) :- 使用
<component>
标签,并通过:is
绑定动态组件名,这样可以根据计算属性component
的值来渲染不同的组件。
- 使用
-
脚本 (
script setup
) :- 引入
useRouter
和computed
。 - 通过
useRouter
获取路由实例,这允许组件访问到路由相关的数据和方法。 - 定义
component
计算属性,用于决定当前应渲染哪个组件。- 遍历
router.routes
数组寻找与router.current.value
匹配的路由路径。 - 如果找到匹配的路由,则返回对应的组件;否则返回
null
。
- 遍历
- 引入
小结:
- 组件通过
:is
动态地决定渲染哪个子组件,这是 Vue 中动态组件的用法。 component
计算属性确保每次路由变化时都能正确渲染与当前路由路径匹配的组件。- 这种设计模式常用于 Vue 应用中的
<router-view>
组件,负责根据当前激活的路由显示正确的组件。
四、使用自定义路由组件
最后,我们需要在主应用中注册和使用这些自定义组件:
import { createRouter, createWebHashHistory } from './grouter/index'
const routes = [
// 定义你的路由规则
]
const router = createRouter({
history: createWebHashHistory(),
routes
})
export default router
import { createApp } from 'vue'
import App from './App.vue'
import router from './router/index'
const app = createApp(App)
app.use(router)
.mount('#app')
这段代码展示了如何在 Vue.js 应用中设置和使用路由。以下是关键点的简要解析:
-
创建路由器:
- 从
./grouter/index
导入createRouter
和createWebHashHistory
。 - 定义路由规则数组
routes
。 - 使用
createRouter
函数创建路由器实例,传入配置对象,包括使用createWebHashHistory
创建的历史记录对象和路由规则。
- 从
-
导出路由器:
- 将创建的
router
实例导出,以便在应用的其他部分使用。
- 将创建的
-
初始化 Vue 应用:
- 从 Vue.js 导入
createApp
函数。 - 导入根组件
App.vue
和上面创建的router
。 - 使用
createApp
创建应用实例,传入根组件App
。 - 使用
.use(router)
方法将创建的路由器实例安装到应用中。
- 从 Vue.js 导入
-
挂载应用:
- 使用
.mount('#app')
方法将应用挂载到 DOM 中具有 idapp
的元素上。
- 使用
小结:
- 这段代码配置了 Vue.js 的路由功能,使用基于 hash 的历史记录机制。
- 路由器实例通过
createRouter
和路由规则数组创建。 - 应用实例通过
createApp
创建,并使用.use()
方法将路由器实例注入应用上下文。 - 最后,应用挂载到 DOM 上,完成整个应用的初始化过程。
五、总结
手写一个简易版的Vue Router不仅加深了我们对前端路由系统的理解,还揭示了Vue Router背后的工作机制。通过定义路由实例、历史模式、组件和钩子,我们可以构建出灵活且可扩展的路由系统,为SPA的开发奠定坚实的基础。
转载自:https://juejin.cn/post/7392248233194111012