第108课:Vue Router 4(上)——路由配置、动态路由、router-link、router-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 函数创建路由实例,通过 createWebHistory 或 createWebHashHistory 选择历史模式。
1 | // router/index.js |
两种历史模式:
| 模式 | URL 格式 | 服务端配置要求 | 适用场景 |
|---|---|---|---|
createWebHistory() |
https://example.com/about |
需要服务端配置(所有路径返回 index.html)。 |
生产环境,SEO 友好,推荐。 |
createWebHashHistory() |
https://example.com/#/about |
无需服务端配置。 | 静态文件托管、无法配置服务端。 |
在 Vue 应用的入口文件中挂载路由:
1 | // main.js |
2. <router-view>:路由视图的出口
<router-view> 是一个函数式组件,它根据当前 URL 匹配到的路由组件进行动态渲染。你可以把它理解为“路由的占位符”——URL 变化时,<router-view> 自动替换为对应页面组件。
1 | <!-- App.vue --> |
一个页面中可以有**多个 <router-view>**,分别渲染不同层级的嵌套路由(通常用于命名视图或嵌套布局)。
3. <router-link>:声明式导航
<router-link> 是 Vue Router 提供的导航组件,渲染为 <a> 标签,但阻止了默认的页面刷新行为。它通过 to prop 指定目标路径。
3.1 基本用法
1 | <template> |
3.2 激活状态样式
当 <router-link> 的 to 与当前 URL 匹配时,Vue Router 自动为其添加 router-link-active 和 router-link-exact-active 两个 CSS 类名。可以通过 CSS 自定义激活样式:
1 | .router-link-active { |
router-link-active 是模糊匹配(当前路径以 to 开头即为激活)。例如 to="/" 在所有路径下都是激活状态。router-link-exact-active 是精确匹配(仅当路径完全相等时才激活)。通常使用 router-link-active 就足够。
3.3 自定义激活类名与元素
可以通过 active-class、exact-active-class 属性自定义类名,通过 custom 属性配合插槽实现完全自定义的导航元素:
1 | <router-link to="/about" custom v-slot="{ href, navigate, isActive }"> |
custom 模式下,<router-link> 不再渲染任何包裹元素,而是通过作用域插槽暴露 href、navigate、isActive 等属性,由开发者完全控制渲染。
4. 动态路由:URL 参数
大多数应用需要根据 ID 访问不同的资源(如 /users/42、/products/101)。在路由路径中使用 :paramName 定义动态段(Dynamic Segment)。
4.1 定义动态路由
1 | const routes = [ |
:userId 表示该位置是一个名为 userId 的路由参数,匹配 /users/ 后跟任意值的路径。
4.2 在组件中读取路由参数
在 Vue 3 组合式 API 中,使用 useRoute 获取当前路由对象:
1 | <!-- UserDetail.vue --> |
注意: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 | <script setup> |
方式二:在路由配置中使用 props: true,将参数作为 Props 传递
1 | const routes = [ |
1 | <!-- UserDetail.vue --> |
props: true 使得组件可以通过 Props 获取路由参数,更符合组件化思维。但仍需配合 watch 处理同一组件内的参数变化。
5. 编程式导航:useRouter
除了声明式的 <router-link>,Vue Router 还提供了编程式导航,通过 JavaScript 代码触发页面跳转。这适用于登录后跳转、表单提交后跳转、按钮回调等场景。
5.1 useRouter 与 router.push
1 | <script setup> |
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 | router.push({ |
state 保存在浏览器的 History API 中,页面刷新后仍然存在。适用于传递不需要暴露在 URL 中的临时数据。
6. 路由命名与别名
6.1 命名路由
为路由赋予 name 属性,可以在导航时使用 { name: 'xxx' } 而非硬编码路径,解耦了 URL 结构与组件之间的绑定:
1 | const routes = [ |
1 | <router-link :to="{ name: 'profile' }">个人资料</router-link> |
当路由路径变更时(如 /user/profile 改为 /account/profile),使用命名路由的 <router-link> 无需修改,路径映射在路由表集中维护。
6.2 别名
别名允许一个路由有多条访问路径。使用 alias 字段:
1 | const routes = [ |
访问 /about 和 /info 都会渲染 About 组件,但 URL 栏中保持访问时使用的路径。
7. 综合实战:博客应用的路由框架
1 | // router/index.js |
1 | <!-- App.vue --> |
1 | <!-- BlogDetail.vue --> |
课后练习
一、概念自测(选择题 / 填空题)
(单选) Vue Router 4 中,哪个组件用于渲染匹配到的路由组件?
A.<router-link>
B.<router-view>
C.<router-outlet>
D.<router-component>(单选) 以下关于动态路由参数
:userId的描述,正确的是?
A.route.params.userId的类型是number。
B. 当导航到同一路由但参数不同时,组件会被销毁并重建。
C. 设置props: true后,参数可以通过组件的 Props 获取。
D. 动态参数必须命名为id。(填空) 在 Vue Router 中,要编程式地导航到命名路由
user-detail并传递参数id: 42,应使用router.push({ ______: 'user-detail', params: { id: 42 } })。(多选) 关于
<router-link>的描述,哪些是正确的?
A. 默认渲染为<a>标签。
B. 当链接匹配当前 URL 时,会自动添加router-link-active类名。
C.toprop 只能是字符串,不能是对象。
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 路由系统。需要创建以下文件:
src/router/index.js:使用createRouter和createWebHistory。定义 6 条路由:/(Home)、/courses(CourseList)、/courses/:courseId(CourseDetail,props: true)、/about(About)、/login(Login)、/:pathMatch(.*)*(NotFound)。使用() => import()懒加载所有页面组件。src/views/Home.vue:包含一个<h2>首页</h2>和一个“查看随机课程”按钮,使用useRouter().push({ name: 'course-detail', params: { courseId: randomId } })。src/views/CourseDetail.vue:defineProps<{ courseId: string }>(),显示<h2>课程详情 #{{ courseId }}</h2>和返回课程列表的<router-link to="/courses">。src/views/CourseList.vue、src/views/About.vue、src/views/Login.vue、src/views/NotFound.vue:各自显示对应的标题文本。src/App.vue:包含一个<nav>导航栏,使用<router-link to="/">和<router-link to="/courses">,通过 CSS 类.router-link-active高亮激活状态(颜色#4f46e5)。下方<main>包含<router-view />。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 等生命周期钩子也不会重新触发。
处理数据更新的两种方式:
watch监听route.params的变化:在组件内使用watch(() => route.params.userId, async (newId) => { /* 根据 newId 重新请求数据 */ })。这样当参数变化时,侦听器回调触发,执行新的数据请求。- **使用
props: true配合watch**:在路由配置中设置props: true,路由参数自动作为组件的 Props 传入。在组件内通过watch(() => props.userId, ...)监听参数变化。Props 方式更符合组件化封装原则——组件通过 Props 声明所需数据,无需直接依赖useRoute。
课后练习答案
一、概念自测答案
B
- 解析:
<router-view>是路由视图的出口。<router-link>是导航组件。<router-outlet>是 Angular 的术语。<router-component>不存在。
- 解析:
C
- 解析:设置
props: true后,route.params会作为组件的 Props 传入。A 错误,参数是字符串类型;B 错误,同一组件内参数变化不会销毁重建;D 错误,参数名可以是任意合法标识符。
- 解析:设置
name- 解析:使用命名路由导航时,需指定
{ name: '路由名', params: { ... } }。
- 解析:使用命名路由导航时,需指定
A、B、D
- 解析:C 错误,
to可以是字符串或对象(包含name/path/params/query)。A、B、D 均为正确描述。
- 解析:C 错误,
二、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、各页面组件。”