Vue3 的路由系统 vue-router 是构建单页面应用(SPA)的核心工具,支持 hashhistory 两种工作模式,提供无刷新的 URL 变化。它允许通过字符串路径或路径对象两种方式定义路由链接,并且支持嵌套路由来创建分层视图结构。vue-router 提供了灵活的路由传参机制,包括 query 参数和 params 参数,以及通过 props 配置直接将路由参数传递给组件的能力。replace 方法和属性使得在不添加新历史记录条目的情况下进行导航成为可能,而编程式路由导航则允许开发者通过 JavaScript 代码控制页面跳转,结合导航守卫实现复杂的导航逻辑。

综上,vue-router 不仅功能丰富,灵活高效,而且通过组合式 API 和简洁的设计,简化了路由管理和导航控制,适用于从简单到复杂的各种 Web 应用程序开发需求。

概述

在 Vue 框架中,路由(route)是指一组键值对(key-value)的关系,它将 URL 路径与特定的组件关联起来。多个路由规则通常由一个称为路由器(router)的管理器来处理,这个管理器负责根据当前的 URL 匹配相应的组件并渲染到页面上。

Vue 路由特别适用于构建单页应用(SPA,Single Page Web Application), 在这种类型的 Web 应用,页面不会完全刷新,而是通过 JavaScript 动态地更新页面的部分内容,从而提供更加流畅的用户体验。

下面是一个简单的路由配置示例,展示了如何将不同的 URL 路径映射到相应的 Vue 组件:

  • 当用户访问 /class 时,路由器会加载并显示班级组件
  • 当用户访问 /subject 时,路由器会加载并显示 学科组件
1
2
3
4
const routes = [
{ path: '/class', component: ClassComponent },
{ path: '/subject', component: SubjectComponent }
];
  • 一般组件:这些是普通的 Vue 组件,可以直接在模板中通过自定义标签来使用它们,例如 <Demo/>
  • 路由组件:这些组件是根据路由规则动态加载的,通常存放在项目的 pagesviews 文件夹内。当用户导航到对应的 URL 时,路由器会找到匹配的路由,并渲染相应的路由组件。

这种机制使得开发者可以创建复杂的应用程序,同时保持良好的代码组织和模块化结构。通过这种方式,每个页面或视图都可以被看作是一个独立的组件,这有助于提高开发效率和维护性。

工作模式

在 Vue3 中,路由的工作模式主要分为两种:history 模式和 hash 模式。这两种模式影响着 URL 的结构以及应用与服务器之间的交互方式。下面是针对这两种模式的详细介绍:

history 模式:适用于前台项目

url 更加美观,路径中不带有 #,更接近传统网站的 url。对于搜索引擎优化(SEO)来说,history 模式通常更有利,因为它生成的 URL 更简洁、更符合传统网站的结构。

1
2
3
4
5
6
import { createRouter, createWebHistory } from 'vue-router';

const router = createRouter({
history: createHistory(),
routes: [...]
});

后期项目上线,需要服务端配合处理路径问题,否则刷新会有 404 错误

hash 模式:适用于后台项目

hash 模式下的 URL 会在路径部分之前加上 # 符号,例如 /path/#/to/resource。这个 # 后面的部分不会被发送给服务器,而是由浏览器解析并交给前端路由进行处理。

1
2
3
4
5
6
import { createRouter, createWebHashHistory } from 'vue-router';

const router = createRouter({
history: createWebHashHistory(),
router:[...]
});

服务端不用处理路径,兼容性更好;URL 带有 # 不太美观,且在 SEO 优化方面相对较差。

to 的两种写法

在 Vue 路由中,to 属性用于指定导航的目标路径或路由。根据不同的使用场景和需求,to 有两种主要写法:字符串(String)和命名路由对象(Object with name and params)。下面是这两种写法的具体介绍。

  • 字符串写法

当目标路径简单且不需要传递参数时,可以直接使用字符串定义 to 属性。这种方式适用于大多数简单的页面跳转

1
<route-link :to="/users">Users</route-link>
  • 对象写法

对于更复杂的情况,比如需要传递参数、查询字符串或命名路由,推荐使用对象的形式来定义 to 属性。这提供了更大的灵活性和可读性。

1
2
3
4
5
<!-- 使用具名路由 -->
<router-link :to="{ name: 'user', params: {userID: 123} }">User</router-link>

<!-- 包含查询阐述 -->
<route-link :to="{ path: '/search', query: { q: 'vue' }}">Search</route-link>

选择哪种方式取决于具体的应用场景。对于更复杂的导航需求,使用命名路由对象通常是一个更好的实践,因为它提供了更多的功能和更好的代码组织。

嵌套路由

嵌套路由允许定义父-子关系的视图结果,可以创建更加复杂和灵活的应用界面,比如带有侧边栏导航的布局,其中主内容区域根据不同的子路径显示不同的组件。

image-20241223105110056

要设置嵌套路由,需要在父路由的component配置中定义一个包含子路由的children属性。每个子路由自身也可以有pathcomponent等属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const routes = [
{
path: '/parent',
component: ParentComponent,
children: [
{
path: 'child1', // 子路径
component: Child1Component
},
{
path: 'child2',
component: Child2Component
}
// 默认子路由,当访问 /parent 时显示
{
path: '',
component: DefaultChildComponent
}
]
}
]

在使用了嵌套路由的组件模板中,可以使用 <router-view> 来指定子组件应该插入的位置。如果一个组件是另一个组件的父级,并且拥有子路由,那么该父级组件模板中应该有一个 <router-view> 元素,用于渲染匹配到的子组件。

例如,在ParentComponent模板中:

1
2
3
4
5
6
<template>
<div class="parent">
<h1>这是父组件</h1>
<router-view></router-view>
</div>
</template>

路由传参

queryparams 是两种不同类型的路由传参方式,它们各有特点,适用于不同的场景。

query 参数

query 参数会显示在 URL 的查询字符串部分,即 URL 末尾问号 ? 开始的一系列键值对。适合在商品列表页面中使用,使用 query 参数来指定当前页码、每页商品显示的商品数量、排序方式或者搜索关键词。

  • 访问:使用 useRoute 组合式 API 的 route.query 对象来读取 query 参数。
  • 设置:通过编程式导航 router.push()<router-link>to 属性来设置 query 参数。
1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- News.vue(父路由) -->
<router-link :to="{
name: '细节',
query: { id: news.id, title: news.title, content: news.content }
}">{{ news.title }}
</router-link>

<!-- Detail.vue(子路由) -->
<ul class="news-list">
<li>编号:{{ route.query.id }}</li>
<li>标题:{{ route.query.title }}</li>
<li>内容:{{ route.query.content }}</li>
</ul>

params 参数

params 参数是路径的一部分,通常用于标识资源的唯一性,如用户 ID或文章 ID。它们是路径中的动态段,用冒号 : 前缀表示。

  • 访问:使用 useRoute 组合式 API 的 route.params 对象来读取 params 参数。
  • 设置:通过编程式导航 router.push()<router-link>to 属性来设置 params 参数。
1
2
3
4
5
6
7
8
9
10
11
// router/index.ts
name: "新闻",
path: '/news',
component: News,
children: [
{
name: "细节",
path: 'detail/:id/:title/:content',
component: Detail
}
]
1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- News.vue(父路由) -->
<router-link :to="{
name: '细节',
params: { id: news.id, title: news.title, content: news.content }
}">{{ news.title }}
</router-link>

<!-- Detail.vue(子路由) -->
<ul class="news-list">
<li>编号:{{ route.params.id }}</li>
<li>标题:{{ route.params.title }}</li>
<li>内容:{{ route.params.content }}</li>
</ul>

路由的 props 配置

对路由组件传递 props,可以将路由参数直接作为 props 传递给目标组件,从而简化数据的传递和组件的使用。props 配置可以接受三种不同的值:

布尔值

  • true:所有 params 参数都会自动转换为 props 传递给组件
  • false:默认值,表示不将 params 作为 props 传递
1
2
3
4
5
6
7
8
// router/index.ts
const routes = [
{
path: '/user/:id',
component: UserComponent,
props: true // 将 :id 作为 prop 传递给 UserComponent
}
];

Detail.vue

1
2
3
<script setup lang="ts" name="News">
defineProps(['id', 'title', 'content']);
</script>

对象(Object)

如果提供一个对象,则该对象会直接作为 props 传递给组件,而不依赖于路由参数。这种方式适用于需要传递静态数据的情况。

1
2
3
4
5
6
7
8
9
10
11
12
// router/index.ts
const routes = [
{
path: '/welcome',
component: WelcomeComponent,
props: {
id: '001',
title: '默认标题',
content: '默认内容'
}
}
];

函数(function)

适用于 query 参数,提供一个函数,可以根据路由信息动态地决定传递哪些 props。这个函数接收当前的 route 对象作为参数,并返回一个 props 对象。这种方式提供了最大的灵活性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// router/index.ts
const routes = [
{
name: "detail",
path:'detail',
component:Detail,
props(route){
return {
id:route.query.id,
title:route.query.title,
content:route.query.content
}
}
}
];

replace 属性

replace 方法允许在导航时不添加新的历史记录条目,当使用 replace 进行导航时,浏览器的前进/后退按钮不会将用户带回前后页面。这在某些情况下非常有用,例如当你想更新URL但不希望用户能够通过浏览器的导航按钮返回到之前的页面状态。

replace 属性是相对于 push 提出的,push 是默认的导航方法,它会在浏览器的历史记录中添加一个新的条目,用户可以通过浏览器的前进/后退按钮在这两个页面之间切换。而 replace 不会添加新的历史记录条目,而是替换当前的历史记录条目。因此,用户不能通过浏览器的后退按钮返回到前一个页面。

应用场景:

  • 登录或注册后的重定向
  • 表单提交后的重定向

编程式路由导航

在 Vue3 中,编程式路由导航允许您脱离 <RouterLink> 实现路由跳转。通过编程式导航,您可以实现更复杂的导航逻辑,比如根据用户交互或应用状态动态地改变页面。vue-router 提供了多种方法来进行编程式导航,这些方法可以让您灵活地管理应用地路由。

useRouter 是一个组合式函数,它返回当前应用的 router 实例,可以用它来调用各种导航方法。首先,导入和使用 useRouter

1
2
import { useRouter } from 'vue-router';
const router = useRouter();

以下是 vue-router 提供的主要编程式导航方法。

  1. router.push():用于导航到一个新的 URL。它会向浏览器的历史记录添加一个新的条目

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    function showNewsDetail(news: NewsInter) {
    router.push({
    name: 'detail',
    query: {
    id: news.id,
    title: news.title,
    content: news.content
    }
    });
    }
  2. router.replace():类似于 push,但不会向历史记录添加新条目,而是替换当前的历史记录条目,参数与 push 相同。

  3. router.go(n):在浏览器历史记录中前进或后退 n 步,n 表示要前进或后退的步数,正数表示前进,负数表示后退。

  4. router.back():等同于 router.go(-1),即后退一步。

  5. router.forward():等同于 router.go(1),即前进一步。