让面试官为你停留 —— Vue 应用中如何实现简易版的 Vue-Router(下篇)
(一)介绍
在前端开发中,路由管理是维持页面状态、导航和组件渲染的核心功能之一。虽然 Vue.js 生态系统中 Vue Router 是处理路由的官方库,但理解其底层原理是提高开发技能的重要步骤。在这篇文章中,我们将逐步构建一个简化版的 Vue 路由器(grouter),帮助读者更深入地理解路由机制。
(二)项目结构与主要文件
在这个示例中,我们构建了一个简化版的Vue-Router,包括以下几个关键文件:
router/index.js- 定义和创建路由实例。App.vue- 根组件,展示如何使用路由链接与视图。main.js- Vue应用入口文件,初始化Vue应用并使用路由。RouterLink.vue与RouterView.vue- 表现层组件,分别对应路由的导航与显示。router/grouter/index.js- 手写的简易路由核心逻辑。
(三)使用 Router Link 和 Router View
RouterLink.vue 和 RouterView.vue 页面
RouterLink 是一个封装好的组件,用于生成锚点标记 <a>,而 RouterView 组件则用于显示当前路由对应的视图组件,这两个组件通过模板引用和渲染完成前端的界面:
<template>
<a :href="'#' + props.to"><slot></slot></a >
</template>
<template>
<component :is="component"></component>
</template>
1. 第一个组件
(1)动态属性绑定 (v-bind 或 :)
- 这里使用
v-bind:href缩写为:href来动态绑定href属性。'#' + props.to是一个字符串拼接表达式,它将#字符与组件的to属性值相连接,生成完整的 URL。这通常用于创建带有特定 URL hash 的锚点链接,常见于使用 hash 路由的单页应用中。
(2)插槽 (<slot>)
<slot>元素用于作为父组件内容的占位符。当在子组件模板中使用<slot>,它会显示从父组件传递过来的内容。这允许父组件自定义子组件的某些部分,提高了组件的复用性和灵活性。
2. 第二个组件
(1)动态组件 (<component>)
<component>是 Vue 中的一个特殊组件,用于根据其is特性的值动态渲染不同的组件。这里的:is="component"表示将根据component属性的值来渲染对应的组件。这在需要根据运行时数据决定渲染哪个组件的场景中非常有用。
(2)组件的动态绑定
- 与第一段代码类似,这里也使用了
v-bind的简写:来动态绑定is特性。component属性的值应该是从父组件传递过来的,通常是一个组件的名称或构造函数。
(四)静态路由链接创建
RouterLink.vue 页面
这段代码的主要作用是定义了一个组件,该组件期望接收一个名为to的字符串类型的prop,且这个prop是必需的。在实际应用中,to prop很可能是用来指定链接的目标路径,比如在RouterLink组件中,用于定义导航到哪个路由。
<script setup>
import { defineProps } from 'vue';
let props = defineProps({
to: {
type: String,
required: true // 这个参数是必须要传递的
}
})
</script>
1. <script setup>语法糖
<script setup>是Vue 3引入的一种新的脚本块语法,它旨在简化组件的脚本定义,使得组件的属性、事件处理器和其他逻辑更紧密地与其模板关联。这种方式省去了传统Vue组件中需要导出setup函数的步骤,使得代码更加直观和简洁。
2. defineProps函数
defineProps是Vue 3中用于声明组件接收的props的一种方法。它接受一个对象,对象的键是props的名称,值是一个对象,描述了prop的类型、是否必需、默认值等。这比Vue 2中直接在props数组或对象中声明更强大和灵活。
3. Props 验证
在defineProps的参数中,我们看到了对to prop的定义:
type: String:指定了toprop的类型必须是字符串。这有助于在开发阶段捕获类型错误,提高代码质量。required: true:指定了toprop是必需的,也就是说,如果在使用该组件时没有传递toprop,Vue将会发出警告,因为这个prop是组件正确运行的必要条件。
(五)动态组件渲染
RouterView.vue 页面
这段代码利用计算属性实现响应式数据处理,以及Vue Router进行路由匹配和组件动态渲染。这些技能点在构建复杂的单页应用中非常重要,它们可以帮助开发者创建响应式、可维护且高效的用户界面。
<script setup>
import { computed, } from 'vue';
import { useRouter } from './index';
// router -> routes / -> component:About
let router = useRouter()
console.log(router,'?????????');
// 这里的 component 是动态的,响应式的,用计算属性实现的
const component = computed(() => {
const route = router.routes.find(
(route) => route.path === router.current.value
)
return route ? route.component : null
})
</script>
1. useRouter函数
useRouter函数是用于获取Vue Router实例的函数,它通过injectAPI从组件的上下文中检索路由实例。这使得组件可以直接访问路由相关的功能,如当前路由、导航等,而无需在每个组件中显式导入或传递路由实例。
2. computed函数
-
在这段代码中,
computed函数接收一个箭头函数,该函数的主体是一个find方法的调用,用于从router.routes数组中查找与当前路由路径相匹配的路由规则。 -
router.routes.find((route) => route.path === router.current.value):这行代码遍历router.routes数组中的每一个路由规则,使用箭头函数作为find方法的回调函数。回调函数接收一个route参数,代表当前迭代的路由规则。如果route.path与router.current.value(当前路由路径)相匹配,find方法将返回该route对象。
(六)定义和创建路由实例
index.js 页面
这段代码的主要作用是设置和初始化一个Vue Router实例,用于管理应用中的导航和组件渲染。通过定义路由规则,我们告了Vue Router哪些URL路径应该对应哪些组件。一旦Router实例创建并配置好,它就能处理页面间的导航和组件的动态加载,这对于构建单页应用(SPA)至关重要。
import { createRouter,createWebHashHistory } from "./grouter/index";
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
1. 自定义 Vue Router
- 代码中首先从自定义的
grouter/index模块导入了createRouter和createWebHashHistory函数。这表明我们正在使用一个自己封装或修改过的Vue Router实现,而不是直接使用官方的vue-router包。这种做法允许我们根据项目需求定制路由管理器的行为,例如,可以调整history模式等。
2. 路由规则定义
- 这里定义了一个路由规则数组,每个规则包括
path、name和component三个属性。path属性定义了URL的路径,name属性给路由命名,component属性则指定了与该路径对应的组件。这是Vue Router中定义路由映射的基本方式。
3. 创建Router实例
- 通过调用
createRouter函数,传入一个配置对象来创建一个Router实例。配置对象包括history和routes两个属性。history属性指定了路由管理器使用哪种历史模式,这里是createWebHashHistory(),意味着使用基于URL hash的模式。routes属性则是上述定义的路由规则数组。
4. 导出路由实例
- 最后,
export default router将创建的Router实例导出,以便在应用的其他部分使用。
(七)Vue应用入口文件
main.js 页面
最后通过app.use(router)将路由器集成到Vue应用中。
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router/index'
const app = createApp(App)
app
.use(router)
.mount('#app')
1. 创建应用实例
- 这里使用
createApp函数创建了一个Vue应用实例,并将App组件作为根组件传递给它。这意味着App组件将成为整个Vue应用的最外层组件。
2. 集成路由
app.use()方法用于安装插件或中间件到Vue应用实例中。在这里,它被用来安装路由插件。一旦路由被安装,应用中的所有组件都可以通过this.$router和this.$route访问到路由实例及其相关功能。
3. 挂载应用
- 最后,
app.mount()方法将Vue应用挂载到DOM中的一个元素上,通常是一个div元素,其id为app。这是启动应用的最后一环,一旦调用,Vue应用就会开始渲染,并将UI呈现到页面上。
(八)总结
通过手写一个简版的 Vue Router,我们不仅能更好地理解 Vue 官方路由的工作原理,还能加深对 Vue 响应式系统、组件化以及插件化架构的理解。这种深入浅出的学习方法,使我们能够在实际项目中更加自如地使用或甚至扩展 Vue Router,源码我将在下一篇文章给大家展示提供。