前言
在之前Vue的实践中,我们已经有了使用前端路由种种特性的经验,比如通过路由实现单页应用的开发、通过路由实现URL与组件的映射关系,通过路由传值实现组件通讯.etc,那么用了这么久的vue-router究竟是怎么开发的呢,今天我们就像自己尝试着写一个vue-router!
草船借箭 举一反三
Vue Router是Vue.js框架的官方路由管理器,它提供了灵活的导航控制、路由嵌套和命名视图等功能。
那么今天我们就可以通过一个简单的小Demo来演示一下vue-router的使用以及我们今天要手写实现的功能。
1. 初始化一个vite+vue项目
这里我们有多种方式来初始化一个vite+vue项目,具体可以根据官方文档来选择自己喜欢的方式。
vite脚手架与vue cli脚手架不同之处我在之前的文章中也有提到过,其中一点就是vite不会帮我们自动安装vue-router等依赖,所以下一步我们还需要手动安装一下vue-router的依赖
2. 安装 vue-router 依赖
同样,我们可以使用不同的包管理工具来安装
npm
npm install vue-router@4
yarn
yarn add vue-router@4
pnpm
pnpm add vue-router@4
3. 基本页面
今天这个Demo很简单,主要就是三个页面 App.vue、About.vue、Home.vue
html
<!-- App.vue -->
<template>
<div class="nav">
<router-link to="/home">去Home</router-link> |
<router-link to="/about">去About</router-link>
</div>
<router-view />
</template>
html
<!-- views/About.vue -->
<template>
<div>
This is About
</div>
</template>
html
<!-- views/Home.vue -->
<template>
<div>
This is Home
</div>
</template>
4. 配置路由
首先我们安装好了vue-router之后,需要在src目录下新建一个router文件夹,并且在其之下新建一个index.js文件用于配置路由。
js
// src/router/index.js
import { createRouter,createWebHashHistory } from 'vue-router'
import Home from '../views/Home.vue'
import About from '../views/About.vue'
const routes = [
{
path: '/home',
name: 'home',
component: Home
},
{
path: '/about',
name: 'about',
component: About
}
]
const router = createRouter({
history:createWebHashHistory(), // history模式 hash模式
routes
})
export default router
在main.js文件中我们引入配置好的路由并且在全局挂载点上use掉使其生效
js
// src/main.js文件
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
const app = createApp(App)
app
.use(router)
.mount('#app')
4. 使用vue-router
到这里,我们的路由配置过程就完成了,接下来我们就可以通过vue-router实现下面这个功能
手写Vue-router
在上面这个例子里面,我们回顾巩固了一下vue-router的使用,首先,我们选择使用的是hash路由模式,其与普通路由的区别就是url后会接一个'#',这个符号虽然看起来不美观,但是官方在打造它的时候为其赋予了一些独有的功能,比如url为#或#后面直接接字符串的话页面并不会发生跳转等等。
今天我们选择使用手写哈希路由,原因就是其相对于普通路由手写起来更加简单一点,为了方便理解其原理,我们就来看看如何手写一个哈希路由实现上面的Demo相同的功能。
创建RouterLink.vue
首先,我们创建一个 RouterLink.vue
组件,用于生成带有链接的文本。该组件的模板如下:
html
<template>
<a :href="'#'+to">
<slot />
</a>
</template>
<script setup>
defineProps({
to: {
type: String,
required: true
}
})
</script>
-
<template>
部分:-
<a :href="'#'+to">
: a标签内通过动态绑定href
属性,将其设置为'#' + to
,这样可以通过点击链接时改变url的哈希部分来触发路由变化。 -
<slot />
: 插槽------这个标签允许在组件标签之间插入内容,使得包裹在这个组件中的内容成为链接的文本内容。
-
-
<script setup>
部分:-
defineProps
: 这是 Vue 3 中的一个新特性,用于定义组件的 props。在这里,我们使用defineProps
定义了一个名为to
的 prop,它的类型是字符串,而且是必需的,即必须传递这个 prop。 -
to: { type: String, required: true }
: 这是通过 props 传递进来的路由路径参数,其中to
prop 的类型为字符串,且设置它为必需。
-
一言蔽之,这个组件接受一个 to
属性,将其拼接到链接的 href
中。通过使用插槽(slot),然后就可以让组件能够包含在链接中的任何文本。
创建自定义Router
在使用vue-router中,我们需要对路由进行注册以及一些功能的引入,那么现在,我们来创建我们自定义的路由器 myRouter
:
js
// myRouter/index.js
import { inject, ref } from 'vue';
const ROUTER_KEY = '_router_';
function useRouter() {
return inject(ROUTER_KEY);
}
function createRouter(options) {
return new Router(options);
}
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);
app.component('router-link', RouterLink);
app.component('router-view', RouterView);
}
}
export {
createRouter,
createWebHashHistory,
useRouter
};
-
useRouter
函数:- 通过
inject
获取在应用中提供的路由实例,其 key 为ROUTER_KEY
,这个 key 在后面的代码中定义为'_router_'
。
- 通过
-
createRouter
函数:- 创建并返回一个新的路由器实例,使用传入的
options
参数进行配置。
- 创建并返回一个新的路由器实例,使用传入的
-
createWebHashHistory
函数:-
创建一个基于哈希的路由历史管理对象。
-
使用了一个内部函数
bindEvents
来绑定hashchange
事件,以便在哈希发生变化时触发回调函数。 -
返回一个对象,其中包含了
bindEvents
函数和当前 URL 的哈希部分。
-
-
Router
类:-
构造函数接受一个包含
history
和routes
的options
对象。 -
初始化
this.history
、this.routes
和this.current
,this.current
使用 Vue 3 的ref
函数创建,用于响应式地保存当前路由路径。 -
在构造函数中调用
this.history.bindEvents
绑定哈希变化事件,以更新当前路由路径。 -
install
方法用于在 Vue 应用中注册路由器实例、router-link
组件和router-view
组件。
-
我们这个自定义路由器实现了Hash路由模式,同时通过 install
方法将 router-link
和 router-view
注册为全局组件,并提供了路由信息的全局访问。
创建RouterView.vue
接下来,我们创建 RouterView.vue
组件,用于映射url与组件之间关系。模板如下:
html
<template>
<component :is="component"></component>
</template>
<script setup>
import { computed } from 'vue';
import { useRouter } from '../myRouter/index.js';
const router = useRouter();
const component = computed(() => {
const route = router.routes.find((route) => {
return route.path === router.current.value;
});
return route ? route.component : null;
});
</script>
-
<template>
部分:<component :is="component"></component>
: 通过将<component>
元素的:is
属性绑定到一个名为component
的变量。使得我们运行页面时可以动态的选择要渲染的组件。
-
<script setup>
部分:-
import { computed } from 'vue';
: 引入 Vue 3 的computed
函数,用于创建计算属性。 -
import { useRouter } from '../myRouter/index.js';
: 引入自定义的路由管理器(myRouter
)中的useRouter
函数,用于获取路由实例。 -
const router = useRouter();
: 使用useRouter
函数获取当前路由实例。 -
const component = computed(() => { ... });
: 使用computed
创建一个计算属性component
,该计算属性通过查找路由配置中与当前路径匹配的路由来获取相应的组件。具体过程是在router.routes
中找到path
与router.current.value
匹配的路由,并返回对应的组件。如果找到匹配的路由,则返回该路由的组件,否则返回null
。
-
总而言之,这个组件使用 computed
计算属性获取当前路由对应的组件,并通过 <component :is="component"></component>
实现动态组件的渲染。使用 useRouter
函数从路由器中获取实例,使得组件能够访问路由信息。
记得在router/index.js文件下引入我们自己的router
js
// src/router/index.js
import { createRouter,createWebHashHistory } from './myRouter'
最后我们来看效果:
总结
到这里,我们成功地搭建了一个简单的Vue Router的Hash路由模式,并实现了组件与URL的映射关系。 要打造一个vue-router,我们首先要
1. 打造 createRouter createWebHashHistory 函数
2. 打造的router要能被 vue 给 use 成功
3. 被 use 后,就可以到拿到vue的实例对象, 也就可以用vue自带的方法
手写这样一个简单的路由器有助于理解Vue Router的原理,并提高对Vue.js框架的深入理解。希望这篇文章对你手写Vue Router的过程有所帮助!
那么到了这里我们今天的文章就结束啦~
更多内容【前端面试】当面试官叫你手写一个JavaScript发布订阅模式