Vue Router 是 Vue.js 应用中用于管理页面导航的核心工具,它提供了简洁的 API 和强大的功能,帮助开发者轻松实现前端路由。在这篇文章中,我们将一步步手写一个 Vue-Router 实现,覆盖核心概念和功能。主要包括 router 的初始化与配置、router-link 和 router-view 的实现,以及一些 Vue 组件的知识。
Vue 组件与插件机制
在手写之前,先了解几个概念
全局组件
在 Vue 中,我们可以使用 app.component 注册全局组件,任何地方都可以直接访问:
app.component('my-component', MyComponent)
如果未注册全局组件,Vue 会把它当作普通的 HTML 标签处理。
插槽与组件复用性
Vue 的插槽功能让我们能够在组件内部定义动态内容。在 router-link 的实现中,我们通过 <slot></slot> 插槽允许用户自定义链接的展示文本或 HTML 元素。
Router 插件与 Vue 的结合
Vue 通过插件机制可以将第三方库或插件整合到 Vue 应用中。使用 app.use(router),实际上是调用 Router 实例的 install 方法,这个方法将 Router 实例挂载到 Vue 应用中,并注册了全局的 router-link 和 router-view 组件。
Vue-Router 的初始化与配置
Vue-Router 是 Vue.js 生态中的一个路由管理工具,它让开发者可以轻松管理应用中的不同页面,基于路径加载不同的组件。下面是我们自定义一个 Vue-Router 的基本步骤。
创建路由
我们首先定义一个工厂函数 createRouter,通过它创建一个路由实例:
export const createRouter = (options) => {
return new Router(options)
}
createRouter 函数接收 options 参数,并返回一个新的 Router 实例。Vue Router 的初始化是通过 createRouter 函数完成的,而不是直接 new Router。这是因为在 Vue 3 中,推荐的做法是通过工厂函数来创建实例,而不是直接使用类的构造函数。这种设计模式可以确保每次创建的 Router 实例都是独立的,避免出现全局状态被共享的问题。
配置 History 模式
Vue-Router 支持不同的路由模式:hash 模式和 history 模式。这里我们手写一个简单的 hash 模式实现:
export const createWebHashHistory = () => {
function bindEvents(fn) {
window.addEventListener('hashchange', fn)
}
return {
url: window.location.hash.slice(1) || '/',
bindEvents
}
}
createWebHashHistory 返回一个包含 url 和 bindEvents 的对象,其中 url 是当前的 hash 路径,bindEvents 用于监听 URL 的变化。
Router 类实现
接下来,我们定义一个 Router 类,来处理路由的核心逻辑:
class Router {
constructor(options) {
this.history = options.history
this.routes = options.routes
this.current = ref(this.history.url)
this.history.bindEvents(() => {
this.current.value = window.location.hash.slice(1)
})
}
install(app) {
app.provide('__router__', this)
app.component('router-link', RouterLink)
app.component('router-view', RouterView)
}
}
this.history:负责管理 URL 状态。
this.routes:存储路由配置信息。
this.current:表示当前的路径,是一个响应式变量,利用 ref 创建。
install(app):通过 app.provide 将 Router 实例注入到应用上下文中,并注册了 router-link 和 router-view 两个全局组件。
全局注入与插件系统
Vue.js 的插件系统通过 app.use() 来使用。我们将路由通过 provide/inject 机制提供给整个应用:
export const useRouter = () => {
return inject('__router__')
}
useRouter 是一个自定义的 Hook,利用 Vue 的 inject 获取我们上面通过app.provide('__router__', this)全局提供的 router 对象。
router-link 和 router-view 实现
接下来我们实现路由的核心组件——router-link 和 router-view。
router-link
router-link 是一个全局组件,负责生成链接,允许用户导航到不同页面。我们实现如下:
<template>
<a :href="'#' + to">
<slot></slot>
</a>
</template>
<script setup>
defineProps({
to: {
type: String,
required: true
}
})
</script>
href:基于 to 属性生成 hash 地址。
<slot>:使用插槽允许用户自定义链接的内容。
router-view
router-view 是另一个全局组件,负责渲染当前路径对应的组件。核心思想是利用 Vue 的动态组件机制 <component :is="component">。实现如下:
<template>
<div>
<component :is="component"></component>
</div>
</template>
<script setup>
import { computed } from 'vue'
import { useRouter } from './index.js'
const router = useRouter()
const component = computed(() => {
const route = router.routes.find(route => route.path == router.current.value)
return route ? route.component : null
})
</script>
component:是一个动态组件,根据当前 URL 渲染不同的组件。
computed:监听当前路径的变化,找到匹配的路由配置并返回相应的组件。
总结
虽然手写的实现较为基础,但它帮助我们深入理解了 Vue-Router 的基本工作原理。
使用方法:
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
const app = createApp(App)
// vue和它生态的对接
app.use(router)
.mount('#app')
import { createRouter,createWebHashHistory } from './grouter/index'
import Home from '../pages/Home.vue'
import About from '../pages/About.vue'
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
// component: () => import('../pages/About.vue')
component: About
}
]
const router = createRouter({
history: createWebHashHistory(),
routes
})
export default router
<template>
<div>
<header>
<nav>
<RouterLink to="/">首页</RouterLink>
<RouterLink to="/about">about</RouterLink>
</nav>
</header>
<main>
<router-view></router-view>
</main>
<footer>
</footer>
</div>
</template>
<script setup>
import RouterLink from './router/grouter/RouterLink.vue';
</script>
<style lang="css" scoped>
</style>
如果你觉得这篇文章对你有帮助,欢迎点赞并关注!