初识Vue-router
# 初识Vue-router
# 1. 前端路由的概念与原理
# 1.1 什么是路由
路由(英文:router)就是对应关系。在前端开发中,路由是指 URL 和组件或页面之间的对应关系。
# 1.2 SPA 与前端路由
SPA(Single Page Application)指的是一个 web 网站只有唯一的一个 HTML 页面,所有组件的展示与切换都在这唯一的一个页面内完成。此时,不同组件之间的切换需要通过前端路由来实现。
结论:在 SPA 项目中,不同功能之间的切换,要依赖于前端路由来完成。
# 1.3 什么是前端路由
前端路由是指 Hash 地址与组件之间的对应关系。通过前端路由,可以实现 URL 和组件的映射关系,从而在不同 URL 下渲染不同的组件。
# 1.4 前端路由的工作方式
前端路由的工作方式通常基于 Hash 地址或 HTML5 的 History API。以下是基于 Hash 地址的前端路由工作方式示意图:
# 1-5 实现简易的前端路由
基于动态组件实现简易的前端路由
- 在
App.vue
中使用<component>
标签,结合comName
动态渲染组件。comName
的初始值为HomeComponent
,表示默认显示HomeComponent
组件。 - 在
App.vue
组件中,为<a>
链接添加对应的 Hash 值,如#/home
和#/about
。当点击这些链接时,浏览器地址栏中的 Hash 值会发生变化。 - 在
created
生命周期函数中,使用window.addEventListener
监听hashchange
事件。当 Hash 地址发生变化时,触发onHashChange
方法。onHashChange
方法根据当前 Hash 值更新comName
的值,从而动态切换要展示的组件。
步骤1:通过 <component>
标签,结合 comName
动态渲染组件
在 App.vue
中使用 <component>
标签,根据 comName
的值动态渲染组件。
<!-- App.vue -->
<template>
<div>
<!-- 动态渲染组件 -->
<component :is="comName"></component>
</div>
</template>
<script>
import HomeComponent from './components/HomeComponent.vue';
import AboutComponent from './components/AboutComponent.vue';
export default {
data() {
return {
comName: 'HomeComponent' // 默认显示的组件名称
};
},
components: {
HomeComponent,
AboutComponent
}
};
</script>
<style>
/* 样式 */
</style>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
步骤2:在 App.vue
组件中,为 <a>
链接添加对应的 hash 值
通过 <a>
标签添加 href
属性,并将其值设置为不同的 Hash 地址。
<!-- App.vue -->
<template>
<div>
<!-- 导航链接 -->
<a href="#/home">Home</a>
<a href="#/about">About</a>
<!-- 动态渲染组件 -->
<component :is="comName"></component>
</div>
</template>
<script>
import HomeComponent from './components/HomeComponent.vue';
import AboutComponent from './components/AboutComponent.vue';
export default {
data() {
return {
comName: 'HomeComponent' // 默认显示的组件名称
};
},
components: {
HomeComponent,
AboutComponent
}
};
</script>
<style>
/* 样式 */
</style>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
步骤3:在 created
生命周期函数中,监听浏览器地址栏中 Hash 地址的变化,动态切换要展示的组件的名称
使用 window.addEventListener
监听 hashchange
事件,根据 Hash 地址动态更新 comName
的值,从而切换组件。
<!-- App.vue -->
<template>
<div>
<!-- 导航链接 -->
<a href="#/home">Home</a>
<a href="#/about">About</a>
<!-- 动态渲染组件 -->
<component :is="comName"></component>
</div>
</template>
<script>
import HomeComponent from './components/HomeComponent.vue';
import AboutComponent from './components/AboutComponent.vue';
export default {
data() {
return {
comName: 'HomeComponent' // 默认显示的组件名称
};
},
components: {
HomeComponent,
AboutComponent
},
created() {
// 监听 hash 变化事件
window.addEventListener('hashchange', this.onHashChange);
// 初始化根据当前 hash 设置组件
this.onHashChange();
},
methods: {
onHashChange() {
// 获取当前 hash 值
const hash = window.location.hash.slice(1);
// 根据 hash 值设置组件名称
switch (hash) {
case '/home':
this.comName = 'HomeComponent';
break;
case '/about':
this.comName = 'AboutComponent';
break;
default:
this.comName = 'HomeComponent';
}
}
},
beforeDestroy() {
// 移除 hash 变化事件监听器
window.removeEventListener('hashchange', this.onHashChange);
}
};
</script>
<style>
/* 样式 */
</style>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# 2. Vue-router的基本使用
# 2.1 什么是 vue-router
vue-router
是 Vue.js 官方的路由管理器,用于创建单页面应用(SPA)。通过 vue-router
,我们可以轻松地在 Vue 应用中实现页面导航和组件切换。
vue-router的官方文档地址:https://router.vuejs.org/zh/ (opens new window)
# 2.2 安装和配置 vue-router
# 2-1 在项目中安装 vue-router
在 vue2 的项目中,可以使用以下命令安装 vue-router
:
npm install vue-router@3 --save
- 注意在 Vue2 的项目中,只能安装并使用 3.x 版本的
vue-router
,vue-router
的 4.x 版本 只适用于 Vue 3。
# 2-2 index.js中创建路由模块
在 src
目录下,新建 router/index.js
路由模块,并初始化如下的代码:
// 导入 Vue 框架
import Vue from 'vue';
// 导入 vue-router
import VueRouter from 'vue-router';
// 使用 vue-router 插件
Vue.use(VueRouter);
// 定义路由配置
const routes = [
// 定义路由规则,示例代码如下
// { path: '/', component: HomeComponent },
// { path: '/about', component: AboutComponent }
];
// 创建路由实例
const router = new VueRouter({
routes // 注入路由配置
});
// 导出路由实例
export default router;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 2-3 main.js中导入并挂载
在 src/main.js
入口文件中,导入并挂载路由模块。示例代码如下:
// 导入 Vue 框架
import Vue from 'vue';
// 导入 App 根组件
import App from './App.vue';
// 导入路由模块
import router from './router';
Vue.config.productionTip = false;
new Vue({
router, // 挂载路由模块
render: h => h(App) // 渲染 App 根组件
}).$mount('#app'); // 挂载 Vue 实例到 id 为 app 的元素上
2
3
4
5
6
7
8
9
10
11
12
13
提示
在进行模块化导入的时候,如果给定的是文件夹,则默认导入这个文件夹下,名字叫做idex.js的文件。
# 2-4 组件中声明路由链接和占位符
在 Vue 应用中,vue-router
提供的 <router-link>
和 <router-view>
是实现页面导航和组件切换的核心元素。
# 1. 路由链接 <router-link>
<router-link>
是用于创建导航链接的组件。它可以生成一个带有 href
属性的 <a>
标签,用于在 Vue 应用中进行页面导航。它的主要功能和特性如下:
to
属性:用于指定导航的目标路由路径。- 自动添加
active
类:当链接指向的路径与当前路由匹配时,会自动添加一个router-link-active
类,这使得你可以为当前活跃的导航链接添加特定的样式,方便用户辨认当前所在的页面。 - 保持单页面应用的特点:点击链接不会导致页面刷新,只会触发 Vue 路由的切换。
示例代码如下:
<template>
<div id="app">
<h1>My Vue App</h1>
<!-- 声明路由链接 -->
<nav>
<router-link to="/" exact>Home</router-link> <!-- 导航到 Home 组件 -->
<router-link to="/about">About</router-link> <!-- 导航到 About 组件 -->
</nav>
<!-- 声明路由占位符 -->
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App'
};
</script>
<style scoped>
nav {
margin-bottom: 20px; /* 设置导航栏底部的间距 */
}
.router-link-active {
font-weight: bold; /* 激活状态的链接字体加粗 */
color: red; /* 激活状态的链接字体颜色变红 */
}
</style>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
在上面的代码中,<router-link to="/">Home</router-link>
和 <router-link to="/about">About</router-link>
声明了两个路由链接。当点击这些链接时,会触发路由切换,但不会导致页面刷新。
# 2. 路由占位符 <router-view>
<router-view>
是用于显示匹配的路由组件的占位符。它会根据当前的路由路径动态渲染相应的组件。它的主要功能和特性如下:
- 占位符:
<router-view>
是一个占位符,它会根据当前路由的匹配情况,显示相应的组件。 - 嵌套路由:可以在子组件中继续使用
<router-view>
来实现嵌套路由,从而构建复杂的页面结构。
示例代码如下:
<template>
<div id="app">
<h1>My Vue App</h1>
<!-- 声明路由链接 -->
<nav>
<router-link to="/">Home</router-link> <!-- 导航到 Home 组件 -->
<router-link to="/about">About</router-link> <!-- 导航到 About 组件 -->
</nav>
<!-- 声明路由占位符 -->
<router-view></router-view>
</div>
</template>
2
3
4
5
6
7
8
9
10
11
12
在上面的代码中,<router-view></router-view>
是一个占位符,用于显示当前匹配的路由组件。当路径为 /
时,<router-view>
会显示 HomeComponent
组件。当路径为 /about
时,<router-view>
会显示 AboutComponent
组件。
通过这种方式,vue-router
实现了页面导航和组件的动态切换,从而使 Vue 应用具备了单页面应用的特性。
# 2-5 index.js中声明路由的匹配规则
在 src/router/index.js
路由模块中,通过 routes
数组声明路由的匹配规则。示例代码如下:
import Vue from 'vue';
import VueRouter from 'vue-router';
import HomeComponent from '../components/HomeComponent.vue';
import AboutComponent from '../components/AboutComponent.vue';
Vue.use(VueRouter);
// `routes` 数组中声明路由的匹配规则
const routes = [
{ path: '/', component: HomeComponent }, // 配置 HomeComponent 对应的路由
{ path: '/about', component: AboutComponent } // 配置 AboutComponent 对应的路由
];
const router = new VueRouter({
routes // 注入路由配置
});
export default router;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 2.3 配置路由图示(静态导入组件)
- 静态导入组件的方式比较简单,直接在路由配置文件中导入需要的组件即可
# 2.4 配置路由图示(动态导入组件)
- 动态导入组件的方式可以在需要时才加载组件,适用于大型项目的优化
# 2.5 全局捕获未匹配路由的配置
在 Vue Router 中,{ path: '*', component: () => import('@/view/404.vue') }
用于捕获所有未匹配的路由,即当用户访问的路径未被任何其他路由规则匹配时,会自动跳转到指定的组件(如 404 页面)。
使用场景:
404 错误页面:
- 当用户访问一个不存在的路径时,显示一个友好的 404 错误页面,提示用户该页面未找到。
- 提供返回主页或其他操作的选项,提升用户体验。
防止用户访问无效路径:
- 避免用户输入错误路径后看到空白页面或不合预期的内容。
- 提高应用的鲁棒性,确保所有的访问路径都能够被正确处理。
开发阶段调试:
- 在开发过程中可以帮助开发者检测到未定义的路径,确保所有的路由都有对应的处理逻辑。
SEO 友好性:
- 有助于搜索引擎识别错误的 URL 并适当处理,避免由于无效路径导致搜索引擎对网站产生不良印象。
这种配置通常被放在路由配置的最后一项,以确保其他具体的路由规则优先匹配。
# 2.6 this.$route
和 this.$router
的区别
在我们平常使用 Vue Router 的时候,this.$route
和 this.$router
是两个非常常用的属性。它们都被挂载在 Vue 实例上,但它们的作用和使用场景有所不同。
# 6-1 this.$route
this.$route
是当前激活的路由信息对象,包含当前路由的所有信息。它是一个只读对象,不能通过它来导航到其他路由。
主要属性
path
:当前路由的路径字符串,例如/home
。params
:一个 key/value 对象,表示动态路由参数。query
:一个 key/value 对象,表示查询参数。name
:当前路由的名称。meta
:当前路由的元数据对象。hash
:当前路由的 hash 值。fullPath
:完整解析后的 URL。matched
:一个数组,包含当前路由的所有嵌套路径片段的路由记录。
注意:meta
是一个对象,可以用来存储与路由相关的任意自定义数据。你可以在 meta
中定义任何你需要的键值对,然后在组件或路由守卫中通过 this.$route.meta
访问这些数据。
常见用途:
- 页面标题:通过
meta.title
来动态设置页面标题。 - 权限控制:通过
meta.requiresAuth
来判断用户是否需要登录才能访问某个页面。 - 面包屑导航:通过
meta.breadcrumbName
来设置面包屑导航的显示名称。 - 角色控制:通过
meta.roles
来控制不同角色用户对页面的访问权限。
- 页面标题:通过
使用示例:
const router = new VueRouter({ routes: [ { path: '/home', component: HomeComponent, meta: { title: '首页', breadcrumbName: '主页', requiresAuth: true, roles: ['admin', 'user'] } } ] });
1
2
3
4
5
6
7
8
9
10
11
12
13
14
route对象综合示例
假设我们在浏览器中访问 URL:http://localhost:8080/home?user=123#info
<!-- RouteInfo.vue -->
<template>
<div>
<h1>当前路由信息</h1>
<p>路径: {{ $route.path }}</p> <!-- /home -->
<p>参数: {{ $route.params }}</p> <!-- {},因为在这个示例中没有动态路由参数 -->
<p>查询: {{ $route.query }}</p> <!-- { user: "123" } -->
<p>名称: {{ $route.name }}</p> <!-- undefined,因为这个路由没有设置名称 -->
<p>元数据: {{ $route.meta }}</p> <!-- {},因为这个路由没有元数据 -->
<p>Hash: {{ $route.hash }}</p> <!-- #info -->
<p>完整路径: {{ $route.fullPath }}</p> <!-- /home?user=123#info -->
<p>匹配记录: {{ $route.matched }}</p> <!-- [object Object],包含当前路由的所有嵌套路径片段的路由记录 -->
</div>
</template>
<script>
export default {
name: 'RouteInfo',
created() {
console.log(this.$route); // 打印当前路由信息到控制台
}
};
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
在上面的示例中,我们使用了 this.$route
来获取当前路由的路径、参数、查询和名称,并在模板中展示这些信息。
# 6-2 this.$router
this.$router
是全局路由实例,包含了路由的所有方法和属性。通过 this.$router
,我们可以编程式地导航到其他路由。
主要方法
push(location)
:导航到一个新的路由。replace(location)
:替换当前路由,不会向 history 添加新记录。go(n)
:在浏览历史中前进或后退 n 步。back()
:后退一页。forward()
:前进一页。
示例演示
<!-- RouterNavigation.vue -->
<template>
<div>
<h1>路由导航</h1>
<button @click="goHome">返回首页</button> <!-- 按钮点击导航到首页 -->
<button @click="goAbout">前往关于页面</button> <!-- 按钮点击前往关于页面 -->
<button @click="goBack">后退</button> <!-- 按钮点击后退 -->
</div>
</template>
<script>
export default {
name: 'RouterNavigation',
methods: {
goHome() {
this.$router.push('/home'); // 使用 push 方法导航到 Home 路由
},
goAbout() {
this.$router.push('/about'); // 使用 push 方法导航到 About 路由
},
goBack() {
this.$router.back(); // 使用 back 方法后退一页
}
}
};
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
在上面的示例中,我们使用了 this.$router
的 push
方法导航到 /home
路由,以及使用 back
方法后退一页。
# 6-3 区别总结
特性 | this.$route | this.$router |
---|---|---|
类型 | 当前激活路由的 信息对象 | 全局路由实例 |
主要用途 | 获取当前路由的相关信息 | 编程式地导航到其他路由 |
可读/可写性 | 只读 | 可调用方法修改路由 |
常用属性/方法 | path , params , query , name , meta , fullPath | push() , replace() , go() , back() , forward() |
使用场景 | 需要访问当前路由的信息时 | 需要编程式导航时 |
总结
this.$route
:用于获取当前路由的信息,适用于需要访问当前路由的路径、参数、查询等信息的场景。this.$router
:用于编程式导航到其他路由,适用于需要在代码中跳转到其他页面的场景。
# 3. 重复路由解决方案
在 Vue.js 项目中,使用 Vue-Router 时,我们通常会使用 this.$router.push
方法来进行路由跳转。然而,当你尝试跳转到当前已处于的路由(即同一路由)时,Vue-Router 可能会抛出一个重复导航的错误。这在 Vue-Router 3.0 及以上的版本中尤为常见。
问题背景
在 Vue-Router 3.0 以上的版本中,push
方法会返回一个 Promise
。当你尝试导航到与当前路径相同的路由时,如果没有提供成功或失败的回调函数,这个 Promise
将会被拒绝,从而导致控制台抛出错误:
Error: NavigationDuplicated //导航重复错误
这个错误并不会影响实际的路由行为,但会污染控制台的日志输出,影响调试体验。
解决方案
我们可以通过重写 Vue-Router 的 push
方法来捕获这个错误并进行处理,以避免它抛出。具体实现如下:
// 保存 VueRouter 原始的 push 方法
const originalPush = VueRouter.prototype.push;
// 重写 VueRouter 的 push 方法
VueRouter.prototype.push = function push(location) {
// 调用原始的 push 方法,并捕获返回的 Promise 的错误
return originalPush.call(this, location).catch(err => err);
};
2
3
4
5
6
7
8
originalPush.call(this, location)
:使用call
方法调用originalPush
,确保this
上下文仍然指向当前的 VueRouter 实例。location
是你想要导航到的目标路由。.catch(err => err)
:捕获Promise
的错误。在这里,我们简单地将错误返回,而不抛出。这就防止了NavigationDuplicated
错误的报错显示在控制台。
适用场景
- 如果你的应用频繁地需要导航到当前已处于的路由(例如刷新页面内容而非跳转),这种情况下
push
操作会经常发生重复导航,使用上述解决方案可以避免不必要的错误提示。 - 这个方法适用于 Vue-Router 3.0 以上的版本,在 Vue 3 中也可以使用相同的思路。