WinddSnow

Vue-Router-4-Part1-Route-Configuration-Dynamic-Routes-Navigation

字数统计: 4.2k阅读时长: 18 min
2026/08/01

第108课:Vue Router 4(上)——路由配置、动态路由、router-linkrouter-view

在单页应用(SPA)中,页面切换不应触发整个文档的重新加载——而是通过 JavaScript 动态替换视图内容,同时更新浏览器 URL。Vue Router 是 Vue.js 官方的路由管理器,它与 Vue 3 深度集成,提供了声明式导航<router-link>)、视图占位<router-view>)、动态路由参数嵌套路由编程式导航useRouter/useRoute)等能力。Vue Router 4 是为 Vue 3 全新设计的版本,API 与 Router 3(Vue 2)保持了概念上的延续性,但底层使用 Composition API 重写,并增加了自动移除冗余路由、增强的导航守卫等特性。本节课将聚焦于 Vue Router 4 的核心基础:创建路由表、映射路径到组件、使用动态参数构建详情页、以及通过 <router-link> 和编程式导航实现页面跳转。


1. 安装与创建路由

1.1 安装 Vue Router 4

1
npm install vue-router@4

Vue Router 4 是 Vue 3 专属版本。Vue Router 3.x 是 Vue 2 的版本,两者 API 相似但不可混用。

1.2 创建路由实例与挂载

Vue Router 通过 createRouter 函数创建路由实例,通过 createWebHistorycreateWebHashHistory 选择历史模式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// router/index.js
import { createRouter, createWebHistory } from 'vue-router';

// 懒加载页面组件(推荐:按需加载,减小首屏体积)
const Home = () => import('../views/Home.vue');
const About = () => import('../views/About.vue');

// 路由表:路径 → 组件的映射
const routes = [
{ path: '/', component: Home },
{ path: '/about', component: About },
];

// 创建路由实例
const router = createRouter({
history: createWebHistory(), // HTML5 History 模式(URL 无 # 号)
routes, // 路由表
});

export default router;

两种历史模式

模式 URL 格式 服务端配置要求 适用场景
createWebHistory() https://example.com/about 需要服务端配置(所有路径返回 index.html)。 生产环境,SEO 友好,推荐。
createWebHashHistory() https://example.com/#/about 无需服务端配置。 静态文件托管、无法配置服务端。

在 Vue 应用的入口文件中挂载路由:

1
2
3
4
5
6
7
8
// main.js
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';

const app = createApp(App);
app.use(router); // 注册路由插件
app.mount('#app');

2. <router-view>:路由视图的出口

<router-view> 是一个函数式组件,它根据当前 URL 匹配到的路由组件进行动态渲染。你可以把它理解为“路由的占位符”——URL 变化时,<router-view> 自动替换为对应页面组件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- App.vue -->
<template>
<div id="app">
<header>
<h1>我的应用</h1>
<nav>
<router-link to="/">首页</router-link>
<router-link to="/about">关于</router-link>
</nav>
</header>

<main>
<!-- 匹配到的路由组件渲染在此处 -->
<router-view />
</main>
</div>
</template>

一个页面中可以有**多个 <router-view>**,分别渲染不同层级的嵌套路由(通常用于命名视图或嵌套布局)。


<router-link> 是 Vue Router 提供的导航组件,渲染为 <a> 标签,但阻止了默认的页面刷新行为。它通过 to prop 指定目标路径。

3.1 基本用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<template>
<nav>
<!-- 字符串路径 -->
<router-link to="/">首页</router-link>

<!-- 对象路径(携带参数) -->
<router-link :to="{ name: 'user', params: { id: 42 } }">
用户详情
</router-link>

<!-- 携带查询字符串 -->
<router-link :to="{ path: '/search', query: { q: 'vue', page: 2 } }">
搜索
</router-link>
</nav>
</template>

3.2 激活状态样式

<router-link>to 与当前 URL 匹配时,Vue Router 自动为其添加 router-link-activerouter-link-exact-active 两个 CSS 类名。可以通过 CSS 自定义激活样式:

1
2
3
4
.router-link-active {
color: #4f46e5;
font-weight: bold;
}

router-link-active模糊匹配(当前路径以 to 开头即为激活)。例如 to="/" 在所有路径下都是激活状态。router-link-exact-active精确匹配(仅当路径完全相等时才激活)。通常使用 router-link-active 就足够。

3.3 自定义激活类名与元素

可以通过 active-classexact-active-class 属性自定义类名,通过 custom 属性配合插槽实现完全自定义的导航元素:

1
2
3
4
5
<router-link to="/about" custom v-slot="{ href, navigate, isActive }">
<a :href="href" @click="navigate" :class="{ 'my-active': isActive }">
关于我们
</a>
</router-link>

custom 模式下,<router-link> 不再渲染任何包裹元素,而是通过作用域插槽暴露 hrefnavigateisActive 等属性,由开发者完全控制渲染。


4. 动态路由:URL 参数

大多数应用需要根据 ID 访问不同的资源(如 /users/42/products/101)。在路由路径中使用 :paramName 定义动态段(Dynamic Segment)

4.1 定义动态路由

1
2
3
4
5
6
7
const routes = [
{
path: '/users/:userId',
name: 'user-detail', // 命名路由:便于编程式导航
component: () => import('../views/UserDetail.vue'),
},
];

:userId 表示该位置是一个名为 userId 的路由参数,匹配 /users/ 后跟任意值的路径。

4.2 在组件中读取路由参数

在 Vue 3 组合式 API 中,使用 useRoute 获取当前路由对象:

1
2
3
4
5
6
7
8
9
10
11
12
<!-- UserDetail.vue -->
<script setup>
import { useRoute } from 'vue-router';

const route = useRoute();
// route.params.userId 是字符串类型(来自 URL)
console.log('用户 ID:', route.params.userId);
</script>

<template>
<h2>用户详情 #{{ route.params.userId }}</h2>
</template>

注意route.params 中的值是字符串,如果需要数字,需要手动转换(Number(route.params.userId))。此外,useRoute() 返回的是一个响应式对象,你可以直接 watch 它的变化来响应同一组件内参数切换(例如从 /users/1 导航到 /users/2,组件不会重新创建,但 route.params 会更新)。

4.3 同一组件内参数变化的响应

当导航到同一个路由但参数不同时(如 /users/1/users/2),Vue 为了性能会复用同一个组件实例(不会销毁再重建)。这意味着 setup 不会重新执行,生命周期钩子也不会重新触发。如果你需要在参数变化时执行逻辑(如重新请求数据),有两种方式:

方式一:watch 监听 route.params

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script setup>
import { useRoute } from 'vue-router';
import { watch, ref } from 'vue';

const route = useRoute();
const user = ref(null);

watch(
() => route.params.userId,
async (newId) => {
user.value = await fetchUser(newId);
},
{ immediate: true } // 首次渲染也执行
);
</script>

方式二:在路由配置中使用 props: true,将参数作为 Props 传递

1
2
3
4
5
6
7
const routes = [
{
path: '/users/:userId',
component: UserDetail,
props: true, // 将 route.params 作为组件的 props 传递
},
];
1
2
3
4
5
6
7
8
9
<!-- UserDetail.vue -->
<script setup>
const props = defineProps({
userId: { type: String, required: true },
});

// 直接使用 props.userId
// 当参数变化时,组件不会重建,但 props 会更新(需要 watch 或使用 computed)
</script>

props: true 使得组件可以通过 Props 获取路由参数,更符合组件化思维。但仍需配合 watch 处理同一组件内的参数变化。


5. 编程式导航:useRouter

除了声明式的 <router-link>,Vue Router 还提供了编程式导航,通过 JavaScript 代码触发页面跳转。这适用于登录后跳转、表单提交后跳转、按钮回调等场景。

5.1 useRouterrouter.push

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<script setup>
import { useRouter } from 'vue-router';

const router = useRouter();

function goToUser(id) {
// 字符串路径
router.push(`/users/${id}`);
}

function goToAbout() {
// 对象路径
router.push({ name: 'about' });
}

function goHome() {
router.push('/');
}
</script>

router.push(to) 向历史记录栈中添加一个新条目,等价于用户点击一个 <router-link>。用户可以点击浏览器“后退”按钮回到之前的页面。

5.2 其他导航方法

方法 行为
router.push(to) 导航到指定路径,向历史记录中添加条目。
router.replace(to) 导航到指定路径,替换当前历史记录条目(不新增记录)。
router.go(n) 在历史记录栈中前进或后退 n 步(router.go(-1) 后退一页)。
router.back() 后退一页,等价于 router.go(-1)
router.forward() 前进一页,等价于 router.go(1)

5.3 携带状态:state

router.push 可以携带一个 state 对象,该对象不会被展示在 URL 中,但可以通过 history.state 获取:

1
2
3
4
router.push({
name: 'checkout',
state: { fromCart: true, cartItems: 3 },
});

state 保存在浏览器的 History API 中,页面刷新后仍然存在。适用于传递不需要暴露在 URL 中的临时数据。


6. 路由命名与别名

6.1 命名路由

为路由赋予 name 属性,可以在导航时使用 { name: 'xxx' } 而非硬编码路径,解耦了 URL 结构与组件之间的绑定:

1
2
3
4
5
6
7
const routes = [
{
path: '/user/profile',
name: 'profile',
component: Profile,
},
];
1
<router-link :to="{ name: 'profile' }">个人资料</router-link>

当路由路径变更时(如 /user/profile 改为 /account/profile),使用命名路由的 <router-link> 无需修改,路径映射在路由表集中维护。

6.2 别名

别名允许一个路由有多条访问路径。使用 alias 字段:

1
2
3
4
5
6
7
8
const routes = [
{
path: '/about',
name: 'about',
component: About,
alias: '/info', // /info 也匹配此组件
},
];

访问 /about/info 都会渲染 About 组件,但 URL 栏中保持访问时使用的路径。


7. 综合实战:博客应用的路由框架

1
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
// router/index.js
import { createRouter, createWebHistory } from 'vue-router';

const Home = () => import('../views/Home.vue');
const BlogList = () => import('../views/BlogList.vue');
const BlogDetail = () => import('../views/BlogDetail.vue');
const NotFound = () => import('../views/NotFound.vue');

const routes = [
{ path: '/', name: 'home', component: Home },
{ path: '/blog', name: 'blog-list', component: BlogList },
{
path: '/blog/:postId',
name: 'blog-detail',
component: BlogDetail,
props: true, // 将 postId 作为 Props 传递给组件
},
{ path: '/:pathMatch(.*)*', name: 'not-found', component: NotFound },
];

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

export default router;
1
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
<!-- App.vue -->
<script setup>
import { useRouter } from 'vue-router';

const router = useRouter();

function goToRandomPost() {
const randomId = Math.floor(Math.random() * 100) + 1;
router.push({ name: 'blog-detail', params: { postId: randomId } });
}
</script>

<template>
<div class="app">
<header>
<h1>📝 我的博客</h1>
<nav>
<router-link to="/">首页</router-link>
<router-link to="/blog">文章列表</router-link>
<button @click="goToRandomPost">随机阅读</button>
</nav>
</header>

<main>
<router-view />
</main>
</div>
</template>
1
2
3
4
5
6
7
8
9
10
11
<!-- BlogDetail.vue -->
<script setup>
const props = defineProps({
postId: { type: String, required: true },
});
</script>

<template>
<h2>文章 #{{ postId }}</h2>
<router-link to="/blog">← 返回列表</router-link>
</template>

课后练习

一、概念自测(选择题 / 填空题)

  1. (单选) Vue Router 4 中,哪个组件用于渲染匹配到的路由组件?
    A. <router-link>
    B. <router-view>
    C. <router-outlet>
    D. <router-component>

  2. (单选) 以下关于动态路由参数 :userId 的描述,正确的是?
    A. route.params.userId 的类型是 number
    B. 当导航到同一路由但参数不同时,组件会被销毁并重建。
    C. 设置 props: true 后,参数可以通过组件的 Props 获取。
    D. 动态参数必须命名为 id

  3. (填空) 在 Vue Router 中,要编程式地导航到命名路由 user-detail 并传递参数 id: 42,应使用 router.push({ ______: 'user-detail', params: { id: 42 } })

  4. (多选) 关于 <router-link> 的描述,哪些是正确的?
    A. 默认渲染为 <a> 标签。
    B. 当链接匹配当前 URL 时,会自动添加 router-link-active 类名。
    C. to prop 只能是字符串,不能是对象。
    D. 可以通过 custom 属性和作用域插槽自定义渲染内容。

二、AI 编程任务:编写面向 AI 的提示词

场景:你需要为一个在线教育平台搭建 Vue Router 4 路由框架。要求如下:

  • 使用 createWebHistory 模式。
  • 路由表包含以下页面:/(首页)、/courses(课程列表)、/courses/:courseId(课程详情,使用 props: true)、/about(关于我们)、/login(登录页)、*(404 页面)。
  • 课程详情组件通过 Props 接收 courseId,并显示课程 ID 和返回课程列表的链接。
  • 在导航栏中使用 <router-link> 构建首页和课程列表的链接,激活状态高亮。
  • 在首页添加一个“查看随机课程”按钮,使用 useRouter 编程式导航到随机的 courseId(1~100)。
  • 所有页面组件使用简单的函数式组件(仅需展示标题或 ID)。

任务要求:请写出一段完整的中文提示词,发送给 AI,使其生成完整的路由配置文件、App.vue 和各页面组件。提示词中需明确指定历史模式、路由表结构、动态路由的 Props 传递和编程式导航。

三、Agent 模式下的提示词示例

你是一个资深前端开发 Agent。请为一个 Vue 3 在线教育平台创建 Vue Router 4 路由系统。需要创建以下文件:

  1. src/router/index.js:使用 createRoutercreateWebHistory。定义 6 条路由:/(Home)、/courses(CourseList)、/courses/:courseId(CourseDetail,props: true)、/about(About)、/login(Login)、/:pathMatch(.*)*(NotFound)。使用 () => import() 懒加载所有页面组件。
  2. src/views/Home.vue:包含一个 <h2>首页</h2> 和一个“查看随机课程”按钮,使用 useRouter().push({ name: 'course-detail', params: { courseId: randomId } })
  3. src/views/CourseDetail.vuedefineProps<{ courseId: string }>(),显示 <h2>课程详情 #{{ courseId }}</h2> 和返回课程列表的 <router-link to="/courses">
  4. src/views/CourseList.vuesrc/views/About.vuesrc/views/Login.vuesrc/views/NotFound.vue:各自显示对应的标题文本。
  5. src/App.vue:包含一个 <nav> 导航栏,使用 <router-link to="/"><router-link to="/courses">,通过 CSS 类 .router-link-active 高亮激活状态(颜色 #4f46e5)。下方 <main> 包含 <router-view />
  6. src/main.js:创建 Vue 应用,使用 app.use(router) 并挂载。
    所有文件添加 JSDoc 注释。完成后列出所有文件内容。

四、面试真题与参考答案

题目(美团前端面试题):

请解释 Vue Router 中动态路由参数的工作机制。当从 /users/1 导航到 /users/2 时,组件是否会重新创建?为什么?如何处理这种情况下的数据更新?请举例说明两种处理方式。

参考答案

当 URL 从 /users/1 变为 /users/2 时,Vue Router 匹配的是同一个路由定义(/users/:userId),因此不会销毁并重建组件实例——这是 Vue Router 的优化行为,避免不必要的销毁和重建开销。组件实例被复用,setup 函数不会重新执行,onMounted 等生命周期钩子也不会重新触发。

处理数据更新的两种方式:

  1. watch 监听 route.params 的变化:在组件内使用 watch(() => route.params.userId, async (newId) => { /* 根据 newId 重新请求数据 */ })。这样当参数变化时,侦听器回调触发,执行新的数据请求。
  2. **使用 props: true 配合 watch**:在路由配置中设置 props: true,路由参数自动作为组件的 Props 传入。在组件内通过 watch(() => props.userId, ...) 监听参数变化。Props 方式更符合组件化封装原则——组件通过 Props 声明所需数据,无需直接依赖 useRoute

课后练习答案

一、概念自测答案

  1. B

    • 解析:<router-view> 是路由视图的出口。<router-link> 是导航组件。<router-outlet> 是 Angular 的术语。<router-component> 不存在。
  2. C

    • 解析:设置 props: true 后,route.params 会作为组件的 Props 传入。A 错误,参数是字符串类型;B 错误,同一组件内参数变化不会销毁重建;D 错误,参数名可以是任意合法标识符。
  3. name

    • 解析:使用命名路由导航时,需指定 { name: '路由名', params: { ... } }
  4. A、B、D

    • 解析:C 错误,to 可以是字符串或对象(包含 name/path/params/query)。A、B、D 均为正确描述。

二、AI 编程任务参考答案(提示词示例)

示例提示词
“请用 Vue 3 + Vue Router 4 搭建一个在线教育平台的路由框架。要求:

  • 使用 createWebHistory
  • 路由:/(Home)、/courses(CourseList)、/courses/:courseId(CourseDetail,props: true)、/about(About)、/login(Login)、*(NotFound)。
  • CourseDetail 通过 Props 获取 courseId,显示标题和返回链接。
  • 导航栏用 <router-link> 并高亮激活状态。
  • 首页添加‘随机课程’按钮,使用 useRouter 跳转随机 ID。
  • 输出所有文件:router/index.js、App.vue、main.js、各页面组件。”
CATALOG
  1. 1. 第108课:Vue Router 4(上)——路由配置、动态路由、router-link、router-view
    1. 1.1. 1. 安装与创建路由
      1. 1.1.1. 1.1 安装 Vue Router 4
      2. 1.1.2. 1.2 创建路由实例与挂载
    2. 1.2. 2. <router-view>:路由视图的出口
    3. 1.3. 3. <router-link>:声明式导航
      1. 1.3.1. 3.1 基本用法
      2. 1.3.2. 3.2 激活状态样式
      3. 1.3.3. 3.3 自定义激活类名与元素
    4. 1.4. 4. 动态路由:URL 参数
      1. 1.4.1. 4.1 定义动态路由
      2. 1.4.2. 4.2 在组件中读取路由参数
      3. 1.4.3. 4.3 同一组件内参数变化的响应
    5. 1.5. 5. 编程式导航:useRouter
      1. 1.5.1. 5.1 useRouter 与 router.push
      2. 1.5.2. 5.2 其他导航方法
      3. 1.5.3. 5.3 携带状态:state
    6. 1.6. 6. 路由命名与别名
      1. 1.6.1. 6.1 命名路由
      2. 1.6.2. 6.2 别名
    7. 1.7. 7. 综合实战:博客应用的路由框架
    8. 1.8. 课后练习
      1. 1.8.1. 一、概念自测(选择题 / 填空题)
      2. 1.8.2. 二、AI 编程任务:编写面向 AI 的提示词
      3. 1.8.3. 三、Agent 模式下的提示词示例
      4. 1.8.4. 四、面试真题与参考答案
    9. 1.9. 课后练习答案
      1. 1.9.1. 一、概念自测答案
      2. 1.9.2. 二、AI 编程任务参考答案(提示词示例)