Vue Router 基本用法

基础

入门

路由的目的是建立 url 和组件之间的映射关系;当 url 发生变化时,组件也随之更新;

创建路由器实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { createMemoryHistory, createRouter } from "vue-router"

import HomeView from './HomeView.vue'
import AboutView from './AboutView.vue'

const routes = [
{ path: '/', component: Homeview },
{ path: '/about', component: AboutView },
]

const router = createRouter({
history: createMemoryHistory(),
routes,
})

history 用来记录 url 和路由的双向映射,这里用的是 createMemoryHistory,它会抛开浏览器的 url,完全自我管理;

如果想要跟浏览器的 url 保持关系,则可使用 createWebHistory 或者 createWebhashHistory;

注册路由器插件

创建好路由器实例化,可以用 app.use(router) 将其注册为插件;

1
2
3
const app = createApp();
app.use(router);
app.mount("#app")

注册该插件后,会完成以下几项工作:

  • 注册需要的组件,如 RouterLink 和 RouterView
  • 添加全局属性,如 $router 和 $route
  • 添加全局组合式函数 useRouter 和 useRoute
  • 解析初始路由

访问路由器和当前路由

在组合式 API 中,可使用 useRouter 和 useRoute 来访问路由器和当前路由;

动态路由匹配

动态匹配:用于将多个路径匹配到同一个组件

1
2
3
4
5
6
import User from "./User.vue"

const routers = {
// 动态参数使用冒号 : 来标识
{ path: "/users/:id", component: User },
}

可在模板中使用 $route 或者 useRoute 来访问当前路径的参数,例如 $route.params.id

1
2
3
4
5
<template>
<div>
User {{ $route.params.id }}
</div>
</template>

路由支持多个参数,例如 /users/:name/posts/:postId

响应路由参数的变化

当路由参数出现变化时,为提高性能,避免重新渲染,会直接复用原先的组件。这意味着组件不会重新创建,因此跟创建有关的 hook 函数例如 onMount 也不会重新执行;

可使用 watch 或者 onBeforeRouteUpdated 来监听变化,并执行相关的操作;

1
2
3
4
5
6
7
8
<script setup>
import { watch } from 'vue'
import { useRoute } from 'vue-router'

const route = useRoute()

watch(() => route.params.id, (newId, oldId) => {...})
</script>
1
2
3
4
5
6
7
<script setup>
import { onBeforeRouteUpdated } from 'vue-router'

onBeforeRouteUpdated(async(to, from) => {
userData.value = await fetchUser(to.params.id);
})
</script>

捕获所有路由

通过路径参数的正则表达式,可以匹配任意的路由

1
2
3
4
const routes = [
{ path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound},
{ path: '/user-:afteruser(.*)', component: UserGeneric },
]

路由的匹配语法

在参数中自定义正则

当两个路径的前缀相同,只是参数的类型不同时,可使用正则来区分它们;

1
2
3
4
5
const routes = [
// 仅匹配数字
{ path: '/:orderId(\\d+)' },
{ path: '/:productName' }
]

但我个人觉得更好的做法是修改前缀,这样更简单清晰

1
2
3
4
const routes = [
{ path: '/order/:orderId' },
{ path: '/product/:name' },
]

可重复的参数

星号 * 表示 0 个或多个

加号 + 表示 1 个或多个

1
2
3
4
5
6
const routes = [
// 匹配 /one, /one/two, /one/two/three
{ path: '/:chapters+' },
// 匹配 /, /one, /one/two 等
{ path: '/:chapters*'}
]

Sensitive 与 Strict 路由配置

默认情况下,路由是不区分大小写的,如需要区分,可添加 sensitive: true 和 strict: true 选项

可选参数

修饰符 ? 可用来标记可选参数,即 0 个或者 1 个;

1
2
3
4
5
6
const routes = [
// 匹配 /users 和 /users/abc
{ path: '/users/:userId?' },
// 匹配 /users 和 /users/123
{ path: '/users/:userId(\\d+)' }
]

嵌套路由

组件通常是嵌套的,这种嵌套关系也可以反映在 URL 上面。此时,可在路由中配置 children 子路由来标记这种嵌套关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const routes = [
{
path: '/user/:userId',
component: User,
children: [
{
// 匹配 /user/:id/profile
path: 'profile',
component: UserProfile
},
{
// 匹配 /user/:id/posts
path: "posts",
component: "UserPosts"
},
{
// 匹配 /user/:id
path: "",
component: UserHome
}
]
}
]

命名路由

路由支持命名,只需将名称备注在 name 字段中即可;

1
2
3
4
5
6
7
const routes = [
{
path: "/user/:username",
name: 'profile',
component: User
}
]

该名称可用于 router-link 中

1
<router-link :to="{ name: 'profile' params: { username: 'erina' } }"></router-link>

建议使用命名路由,一来这样更方便维护,避免后续因为更改路径,导致很多地方需要跟着改动;二来好的名称也比路径也更容易理解;

路由的命名需要全局唯一,不然会出现冲突;

编程式导航

导航到不同的位置

router.push(…) 会跳转到新的页面,它同时会向页面 history 的栈中添加记录,这样当用户点击浏览器上面的按钮时,就会返回上一页;

点击 router-link 组件也会有相同的效果;

1
<router-link :to="..."></router-link>

router.push 可以支持很多种格式,其参数可以是简单的 url,也可以是一个对象;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 字符串路径
router.push('/users/eduardo')

// 带有路径的对象
router.push({ path: '/users/eduardo' })

// 命名的路由,并加上参数,让路由建立 url
router.push({ name: 'user', params: { username: 'eduardo' } })

// 带查询参数,结果是 /register?plan=private
router.push({ path: '/register', query: { plan: 'private' } })

// 带 hash,结果是 /about#team
router.push({ path: '/about', hash: '#team' })

替换当前位置

router.replace 会跳转新页面,作用与 router.push 相同,区别是直接替换当前路由在 history 中的位置,而不是 push;

横跨历史

在 history 栈中进行跳转,类似 window.history.go(n)

1
2
3
4
router.go(1) // 前进1页,与 router.forward 作用相同
router.go(-1) // 后退1页,与 router.back 作用相同
router.go(3) // 前进3页
router.go(-3) // 后退3页

命名视图

给视图进行命名,可以让它们同时同级展示,而不是嵌套展示

1
2
3
<router-view name="leftSidebar" />
<router-view /> <!-- 没有名称,默认为 default -->
<router-view name="rightSidebar" />

当页面上存在多个视图时,需要给各视图映射相应的组件;

1
2
3
4
5
6
7
8
9
10
11
12
13
const router = ({
history: createWebHashHistory(),
routes: [
{
path: "/",
components: {
default: Home,
leftSidebar: LeftSidebar,
rightSidebar: RightSidebar,
}
}
]
})

命名视图也是支持嵌套滴;

重定向和别名

重定向

重写向:点击 A 路径,重定向跳转到 B 路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 可以是 url
const routes = [{ path: "/home", redirect: "/" }]

// 或者一个对象
const routes = [
{ path: "/home", redirect: { name: "homepage" } }
]

// 或者一个函数
const routes = [
{
path: '/search/:searchText',
redirect: to => {
return {
path: '/search', query: { q: to.params.searchText }
}
}
}
]

相对重定向

1
2
3
4
5
6
7
8
const routes = [
{
path: '/users/:userId/posts',
redirect: to => {
return 'profile' // 实际结果为 /users/:userId/profile
}
}
]

别名

通过别名,可将任意指定的 url 匹配到相应的组件,而不会受到嵌套结构的限制;

路由组件传参

如果在组件中读取 $route 的参数,那么意味着使用该组件,将会与 url 强绑定;组件的复用范围受到了很大的限制;解决方法就是不使用 $route,而是给组件传递参数;

1
2
3
4
5
6
<!-- 传统的写法,组件与url紧密耦合 -->
<template>
<div>
User {{ $route.params.id}}
</div>
</template>
1
2
3
4
5
6
7
8
9
10
11
12
<!-- 新的写法, id 引用 props,由外部传入,而不是读取路由 -->
<script setup>
defineProps({
id: String, // 声明 props 参数
})
</script>

<template>
<div>
User {{ id }}
</div>
</template>
1
2
3
4
// 在路由配置中,通过 props:true 选项将 params 声明为 props
const routes = [
{ path: "/user/:id", component: User, props: true }
]

如果路由映射了多个命名视图,那么需要为每个视图单独备注是否启用 props

1
2
3
4
5
6
7
8
9
10
const routes = [
{
path: "/user/:id",
components: { default: User, sidebar: Sidebar },
props: {
default: true, // 启用
sidebar: false, // 不启用
}
}
]

另外 props 还支持对象或函数类型;

匹配当前路由的链接

有时候多个 router-link 在页面上面会以列表的形式出现,此时经常用不同的颜色,来标识当前激活的 link;

此时需要有一个方法来判断当前处于激活状态的是哪个链接;

不同的历史记录模式

有三种历史模式

  • Hash 模式:会在 URL 上面添加 # 符号,好处是用户重新刷新页面也不要紧,能够正常处理;
  • HTML 模式:URL 跟普通网页的 URL 一模一样,缺点当用户刷新时,会向服务器发送页面请求,需要服务器有相应的处理,不然会出现 404
  • Memory 模式:URL 只保存在内存,不在浏览器的 URL 上面体现,缺点是无法使用浏览器的前进和后退,因为没有页面栈;

Vue Router 基本用法
https://ccw1078.github.io/2023/08/18/Vue Router 基本用法/
作者
ccw
发布于
2023年8月18日
许可协议