浅聊一下
vue-router想必大家都用的不少,但是你想必是没有看过vue-router的底层源码的,本篇文章将带掘友们来了解一下vue-router到底是怎样来实现的...
进入正题
首先我们使用vite脚手架创建好Vue项目,这里我就不过多赘述...
使用 npm i vue-router@4
在终端下载vue-router
使用vue-router
在src目录下创建文件夹router,添加index.js文件,配置好我们的Home.vue和About.vue
js
import {createWebHistory,createRouter} 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(),//history模式 hash模式
routes
})
export default router
抛出router以后,在main.js中需要引入并且use掉
js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
createApp(App)
.use(router)
.mount('#app')
再在App.vue中使用路由,实现路由跳转
js
<template>
<div class="nav">
<router-link to="/">Home | </router-link>
<router-link to="/about">About</router-link>
</div>
<router-view />
</template>
<script setup>
</script>
<style lang="css" scoped>
</style>
点击Home去到home页面,点击About去到about页面
接下来,我们将不使用我们下载的vue-router,而是自己手敲一个vue-router,实现页面跳转
使用myRouter
在敲代码之前,先来观察一下我们需要写点什么
js
import {createWebHashHistory,createRouter} from './myRouter'
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(),//history模式 hash模式
routes
})
export default router
引入中,我们从myRouter引入了 createWebHashHistory和createRouter
createRouter( )
不难看出,我们这里是在创建一个router的实例对象
js
const router = createRouter({
history:createWebHashHistory(),//history模式 hash模式
routes
})
所以,createRouter()的返回值是一个Router对象,那么,createRouter()方法就该这么写
js
function createRouter(options) {
return new Router(options)
}
此时我们还暂未定义Router对象,所以再来定义一下
js
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) {
console.log(app);
app.provide(ROUTER_KEY, this)
// 注册全局组件router-link
app.component('router-link', RouterLink)
app.component('router-view', RouterView)
}
}
这里使用的是ES6的语法,使用constructor来创建类,其中的参数由前面createRouter()中传入的值可以知道是一个history,一个routes,并且我们还要知道当前的url是什么才可以跳转,所以还有一个current。那么这个bindEvents是个干什么用的呢?这个待会再讲,先来看看下面那一坨install()
是干什么用的...
我们在index.js中抛出了一个router对象,在main.js中引入并且use掉vue-router才能正常工作,那么是什么阿猫阿狗都能被use的吗?当然不行,vue规定了,只有有install()
的才可以被use,我们可以在install()中注册全局组件,里面的'router-link'
,router-view
就是全局组件,
createWebHashHistory( )
createHashHistory( )主要是返回一个当前的url
js
function createWebHashHistory() {
function bindEvents(fn) {
window.addEventListener('hashchange', fn)
}
return {
bindEvents,
url: window.location.hash.slice(1) || '/'
}
}
bindEvents接收一个回调函数,一旦url的地址改变,就触发回调函数,createHashHistory( )返回了bindEvents方法和url,然后在Router中调用
js
this.history.bindEvents(() => {
this.current.value = window.location.hash.slice(1)
})
一旦url发生变化,立马更新当前的current
useRouter( )
在useRouter中返回了一个inject,他和provide是一对父子传值组件
provide
是一个在父组件中调用的方法,用于提供数据给后代组件。它接收两个参数,key
是一个唯一的标识符,用于区分不同的提供者,value
则是要提供给后代组件的值。我们需要在前面定义一个key
js
const ROUTER_KEY = '_router_'
inject
是一个在子组件中调用的方法,用于从父组件中获取提供的数据。它接收两个参数,key
是要获取的数据的标识符,defaultValue
是当未找到对应的提供者时的默认值。
js
function useRouter() {
return inject(ROUTER_KEY)
}
router-link
在前面的例子里,要让页面跳转,我们得放上<router-link />
,这是一个全局组件,我们来看看他怎么写的...
html
<template>
<a :href="'#'+to">
<slot />
</a>
</template>
<script setup>
defineProps({
to: {
type: String,
required: true
}
})
</script>
实际上router-view的底层就是a标签,require为true要求必须传入一个String类型的值作为跳转路径,使用插槽来添加想要的内容
router-view
要想让页面显示,就得先放上<router-link />
,这又是怎么实现的呢?
js
<template>
<component :is="component"></component>
</template>
<script setup>
import { computed } from 'vue';
import { useRouter } from '../myRouter/index.js'
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>
通过 :is
属性将 component
变量绑定到 <component>
标签上,这样就可以根据 component
的值动态渲染不同的组件。
在 <script setup>
中,首先导入了 computed
和自定义的 useRouter
函数。然后通过 useRouter()
获取到一个路由实例 router
。
接下来,定义了一个计算属性 component
,它使用 computed
函数来计算动态组件的值。计算属性的逻辑是根据当前的路由路径从 router.routes
中找到对应的路由对象 route
,然后返回该路由对象的 component
属性值。
在这段代码中, router.routes
是一个包含了所有路由信息的数组,每个路由对象都有 path
和 component
属性。通过遍历 router.routes
,找到与当前路由路径相匹配的路由对象,并获取其 component
属性,用于动态渲染组件。
最后,将计算属性 component
绑定到 <component>
标签的 :is
属性上,使得组件能够根据 component
的值动态渲染不同的组件。
到此一个简单的vue-router就完成了...
结尾
Vue-Router的源码是vue中较为简单的一部分,要学懂其他的还得再沉淀沉淀...