我们平时写代码离不开使用路由来改变url而不刷新页面。你肯定知道怎么使用它,但是你知道它是如何实现的吗?本文将带你自己写一个简易版的Vue Router,让你初步了解它的原理是什么。
Vue Router
创建项目
我们首先创建一个叫simple-router的项目:
安装Vue Router
打开终端输入命令安装Vue Router(我这里的项目需要自己安装,我使用的是yarn):
各个文件的配置
main.js 引入并使用路由
javascript
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
createApp(App).use(router).mount('#app')
router目录下的index.js 为页面配置路由
javascript
import {createRouter,createWebHashHistory} from 'vue-router'
import Home from '../views/Home.vue'
import About from '../views/About.vue'
const routes = [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/about',
name: 'about',
component: About
}
]
const router = createRouter({
history: createWebHashHistory(),
routes
})
export default router
App.vue
xml
<template>
<div nav>
<router-link to="/">Home</router-link>
|
<router-link to="/about">About</router-link>
</div>
<router-view />
</template>
Home.vue
xml
<template>
<div>
home page
</div>
</template>
About.vue
xml
<template>
<div>
about page
</div>
</template>
页面效果如下,点击能去到相应页面:
现在我们不使用安装的Vue Router,手写来实现它的功能。
基于Hash实现Vue Router
步骤
首先我们要知道刚刚我们使用路由有哪些步骤:
- 创建一个路由实例,并定义路由规则('router/index.js'中)
- 在Vue 主应用实例中,引入并使用创建的路由实例('main.js'中)
- 在模板中使用
<router-view>
组件来渲染匹配当前 URL 的组件('App.vue'中) - 在模板中使用
<router-link>
组件来创建可以点击的链接,使用户能够导航到不同的路由('App.vue'中)
这些步骤中,创建路由实例用到的createRouter
,createWebHashHistory
方法,使用路由实例用到的useRouter方法,还有<router-view>
, <router-link>
组件,都是Vue Router中的。那么我们要实现的就是它们。
我们在router目录下新建一个myRouter文件夹,在里面新建index.js,RouterLink.vue,RouterView.vue文件,我们的功能将在这里面实现。
将router/index.js中引入的'vue-router'修改为'./myRouter':
javascript
import { createRouter,createWebHashHistory } from './myRouter'
路由实例
我们来看myRouter/index.js:
javascript
import { inject,ref } from 'vue'
const ROUTER_KEY = '__router__'
export function useRouter(){
return inject(ROUTER_KEY)
}
export function createRouter(options){
return new Router(options)
}
export function createWebHashHistory(){
function bindEvents(fn){
window.addEventListener('hashchange', fn)
}
return {
bindEvents,
url: window.location.hash.slice(1) || '/'
}
}
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_KEY, this)
}
}
解释一下这段代码:
我们要用createRouter函数来创建路由实例,那我们就需要有一个Router类并通过它创建实例,createRouter函数接受一个options
对象作为参数并将这个参数传递给Router类,当我们调用createRouter函数时,就会返回一个通过 new Router类创建出来的实例。
这个options参数就是我们创建一个路由实例时传进去的对象,比如说router/index.js中我们创建路由实例时是这样的:
createWebHashHistory函数用于创建一个基于hash模式的路由历史。里面有一个bindEvents方法,接受一个回调函数fn,它用于绑定 hashchange
事件监听器,当URL发生了改变时就会触发回调函数fn。它返回一个对象,其中包含bindEvents
方法和 url
属性。url
属性表示当前的 URL,它从 window.location.hash
中获取,slice(1)
去除了开头的 #
符号。
Router类是一个路由器类,负责管理路由的核心逻辑。它在构造函数constructor中接受一个 options 对象,其中包含 history
和 routes
属性。history
对象用于管理路由历史,routes
是一个路由规则数组。这里实际上history的值就是我们上面写的createWebHashHistory()调用结果,routes的值就是我们之前配置各个页面路径的数组。current
是一个响应式的引用,表示当前的路径,当路径发生改变时,它的值也会随之更新。在构造函数中,它将 history.url
的值赋给 current.value
,并通过调用 history.bindEvents
来绑定 hashchange
事件监听器,以便在 URL 改变时更新 current.value
。
其中install
方法是必须要有的,app.provide(ROUTER_KEY, this)
语句是在 Vue 应用程序中将路由实例注册为一个全局的依赖,以便在全局使用。它使用了 Vue 3 中新增的 provide/inject
API。具体来说,provide
方法用于在 Vue 应用程序中注册一个全局依赖,可以在所有后代组件中访问。它接受两个参数:依赖名称和依赖值。在这段代码中,依赖名称为 ROUTER_KEY
,依赖值为当前的路由实例 this
。这意味着,在任何需要访问路由实例的组件中,都可以使用 inject
方法来注入该实例。
useRouter
函数是一个自定义的实用函数,用于在组件中获取路由实例。它使用了 inject
函数来注入在父组件provide
方法中注册的路由实例。当我们在页面中需要使用路由实例时,只需要调用useRouter方法就可以拿到路由实例了。
<router-link>
这个功能的实现在RouterLink.vue中:
xml
<template>
<a :href="'#' + to">
<slot /> // 插槽,让我们使用RouterLink标签时中间能够放东西
</a>
</template>
<script setup>
defineProps({
to: {
type: String,
required: true // 表示to为必传项
}
})
</script>
我们还需要将其注册为全局组件以便它能在各处使用,那么就需要在myRouter/index.js中引入它并为它注册,RouterView也同样:
引入:
在Router类的install方法中注册为全局:
<router-view>
xml
<template>
<component :is="component"></component>
</template>
<script setup>
import { computed } from 'vue'
import { useRouter } from '../myRouter'
const router = useRouter() // 在当前组件注入了router路由实例
const component = computed(() => {
// 如果当前的url是'/',就返回Home.vue
const route = router.routes.find((route) => {
return route.path === router.current.value
})
return route ? route.component : null
})
</script>
解释一下这段代码:
代码中的模板部分使用了动态组件 <component>
,并绑定了一个 is
属性,该属性的值为一个计算属性 component
,如下:
component
使用了 computed
函数来定义,并返回一个根据当前 URL 找到的对应组件的名称。具体来说,它首先调用 useRouter
函数来获取当前的路由实例 router
,然后遍历该路由实例的 routes
属性,找到其中 path
属性与当前 URL 中的路径相同的那个路由规则。如果找到了对应的路由规则,则返回该规则的 component
属性,即对应的组件名称;否则返回 null
。
最终,component
的值作为 <component>
组件的 is
属性的值,从而动态渲染对应的组件。
现在我们可以使用这两个标签了,在App.vue中修改:
xml
<template>
<div nav>
<RouterLink to="/">Home</RouterLink>
|
<RouterLink to="/about">About</RouterLink>
</div>
<RouterView />
</template>
现在来到页面上,可以正常跳转:
总的来说,我们的代码简单地实现了一个基本的 Vue Router 功能,包括获取路由实例、创建路由实例和创建基于 hash 的路由历史。这是一个较为简化的示例,实际的 Vue Router 功能更为复杂和全面,涉及到更多的特性和配置。如果你想要更深入地了解 Vue Router,可以去查阅官方文档哦。
本文到这就结束啦,欢迎下次再来一起学习ヾ(◍°∇°◍)ノ゙!!