主题
路由进阶(一)
本页对应 路由进阶,共 8 段视频,约 1 小时 45 分。
学习目标
- 能使用
router-link编写声明式导航链接,并验证to属性与路由规则path的对应关系。 - 能区分
router-link-active与router-link-exact-active两个类名,并通过切换路由验证其激活匹配差异。 - 能通过
linkActiveClass、linkExactActiveClass配置自定义链接高亮类名,并验证样式生效。 - 能使用查询参数传参,在目标组件中通过
$route.query读取参数并正确渲染。 - 能配置动态路由参数,在目标组件中通过
$route.params获取参数并展示对应内容。 - 能使用动态路由参数可选符
?配置可选参数,并验证有参、无参两种访问场景。
知识点
需求场景:为什么需要声明式导航
做导航高亮时,如果直接用 a 标签,点击后虽然能跳转,但我们要手动给当前链接加样式,还要移除上一个链接的样式。链接一多,维护成本立刻上来,容易漏改、错改。笔记里的效果就是:当前在哪个导航项,哪个导航项就高亮。

所以这里的核心思路不是“自己管理高亮”,而是把跳转和选中状态交给 vue-router 提供的全局组件 router-link。它替代 a 标签,既能跳转,又能默认提供高亮类名。
router-link 的基本用法:能跳转
为什么需要:路由页面切换需要改变哈希地址,地址变化后再匹配路由规则,最后在 router-view 位置展示组件。转写稿里反复强调:点击链接后,第一步是地址栏变化;第二步拿地址去和规则匹配;第三步在路由出口展示对应组件。
怎么想:router-link 本质还是 a 标签,但跳转地址不写在 href 里,而是写在 to 属性里。to 必须写,而且写的是路由规则中配置的 path 值,不需要写 #。
怎么做:语法是:
vue
<router-link to="path的值">发现音乐</router-link>本节示例代码保留如下:
vue
<div>
<div class="footer_wrap">
<router-link to="/find">发现音乐</router-link>
<router-link to="/my">我的音乐</router-link>
<router-link to="/friend">朋友</router-link>
</div>
<div class="top">
<!-- 路由出口 → 匹配的组件所展示的位置 -->
<router-view></router-view>
</div>
</div>易错点:to 的路径必须和路由规则里的 path 一致。转写稿调试时提到,如果单词写错,点击后地址栏虽然变了,但匹配不到任何规则,下面区域就会一片空白。所以不要少字母、不要多写 #、不要把 / 写成别的符号。
高亮类名:能高亮
为什么需要:导航高亮是常见交互。如果自己写,就要监听点击、记录当前项、切换 class,逻辑分散。router-link 默认就会给当前激活链接加高亮类名,我们只负责写样式即可。
怎么想:使用 router-link 跳转后,当前点击的链接默认会加上两个 class 值:router-link-exact-active 和 router-link-active。我们不需要手动加 class,只需要在 CSS 中给其中任意一个类名设置高亮样式。
怎么做:例如在样式中写:
css
.router-link-active {
color: red;
}或者使用另一个类名也可以。笔记明确说:可以给任意一个 class 属性添加高亮样式即可实现功能。
易错点:不要把高亮类名写到普通 a 标签上,也不要在模板里手动绑定“当前项”状态。先确认 router-link 是否真的渲染成了链接、to 是否匹配成功,再检查样式选择器有没有写对。
运行过程与调试思路
转写稿里有一段很关键的推导:点击“我的音乐”时,地址栏先变成对应路径;然后程序拿这个地址去和路由规则匹配;匹配成功后,在 router-view 所在位置展示对应组件。也就是说,router-link 负责改地址,router-view 负责占位展示,高亮则由默认类名完成。
调试时按这个顺序查:第一,看点击后地址栏有没有变化;第二,看 to 的值和规则 path 是否一致;第三,看 router-view 是否放在希望展示组件的位置;第四,看高亮样式有没有选中默认类名。这样比盲目改样式更快。
小结
router-link 是什么?它是 vue-router 提供的全局组件,用来替代 a 标签。怎么用?写 <router-link to="路径">文本</router-link>,路径与路由规则一致,无需 #。好处是什么?既能跳转,又能默认提供高亮类名,省去手动维护当前导航样式的麻烦。记住:to 必须写、路径必须对、高亮样式写给默认类名即可。
先看现象:router-link 会自动加两个类名

本节笔记里的核心现象是:使用 <router-link></router-link> 做声明式导航时,Vue Router 会自动给“当前导航”加上两个类名。笔记中保留了最基础的写法:
html
<router-link to="/my">我的</router-link>为什么需要:做导航菜单时,用户点到哪里、当前处在哪个页面,需要有高亮提示。如果每次自己判断地址、手动加 class,代码会分散且容易漏。
怎么想:router-link 不是普通 <a>,它会根据当前地址和 to 的匹配结果,自动维护表示“激活”的类名。
怎么做:写 router-link 时把 to 写成路由规则里的 path,例如 to="/my",然后在浏览器开发者工具里审查生成的链接元素,观察 class 变化。
易错点:不要手写 router-link-active 或 router-link-exact-active 去“假装激活”;先确认 to 与路由规则中的 path 一致,再看类名是否出现。
router-link-active:模糊匹配,父级菜单常用
笔记明确写着:router-link-active 是模糊匹配(用的多)。
html
<router-link to="/my">我的</router-link>当 to="/my" 时,可以匹配:
text
/my
/my/a
/my/b
...也就是说,只要当前地址是以 /my 开头的路径,都可以和 to="/my" 匹配到。
为什么需要:真实项目里常有二级、三级路由。例如进入“我的音乐”下的某个子页面时,左侧或顶部的一级菜单“我的音乐”仍应保持高亮,否则用户会觉得导航丢了。
怎么想:router-link-active 看的是“当前路径是否落在 to 所表示的范围里”,所以它适合父级导航、栏目级导航。
怎么做:保留 to="/my",分别访问 /my、/my/a、/my/b,审查该链接的 class,会看到 router-link-active 仍然存在。
易错点:访问 /my/a 时父级链接还高亮,这是模糊匹配的正常结果,不是 bug;不要把“父级还亮着”误判为类名加错。
router-link-exact-active:精确匹配,只认完全相等
笔记对第二个类名的定义是:router-link-exact-active 是精确匹配。
html
<router-link to="/my">我的</router-link>to="/my" 仅可以匹配:
text
/my为什么需要:有些导航只希望在“完全相等”的地址上高亮,例如首页、当前栏目本身。若只用模糊匹配,子页面也会把父级点亮,某些视觉设计下会显得不精确。
怎么想:router-link-exact-active 判断的是当前路径是否与 to 完全一致,不把 /my/a 算作 /my。
怎么做:访问 /my 时,该链接通常同时具备 router-link-active 和 router-link-exact-active;访问 /my/a 时,只保留 router-link-active,不再有 router-link-exact-active。
易错点:类名拼写很长,router-link-exact-active 不要少写 exact;也不要把它和 router-link-active 混为一谈。
在地址栏输入二级路由查看类名添加
笔记第 3 点要求“在地址栏中输入二级路由查看类名的添加”。结合转写稿中路由运行过程的讲解:点击 router-link 后,地址栏先变化,然后地址与路由规则匹配,最后在 router-view 出口位置渲染对应组件。类名的高亮也是围绕“当前地址的匹配结果”产生的。
为什么需要:只看静态代码很难理解两个类名的差别,必须让地址从 /my 变成 /my/a、/my/b 去观察。
怎么想:先固定 to="/my",再改变地址栏中的当前路径;当前路径是“完全等于 /my”还是“以 /my 开头”,决定两个类名是否同时出现。
怎么做:打开页面并审查 router-link 生成的链接元素,依次访问 /my、/my/a、/my/b,对比 class 列表。转写稿也提醒过:二级路由的访问地址不是随便写一个子路径,而是按父子规则拼接出来的,调试时要使用规则里真实存在的地址。
易错点:地址写错、少了斜线、父子路径拼接不对时,路由规则匹配不上,页面可能空白,类名观察也会失真;先保证路由能正常显示,再看高亮类名。
总结:两个类名怎么区分
为什么需要总结:面试和项目里常问“router-link 自动加的两个类名有什么区别”,本质是问模糊匹配与精确匹配。
怎么想:router-link-active 管“范围”,只要当前地址以 to 开头就激活;router-link-exact-active 管“完全相等”,只认一模一样。
怎么做:父级栏目高亮优先理解 router-link-active;需要只在高亮完全匹配项时理解 router-link-exact-active。笔记中的结论就是:router-link 会自动给当前导航添加两个类名,区别在于一个模糊匹配、一个精确匹配。
易错点:to="/my" 能匹配 /my/a 是 router-link-active 的行为;router-link-exact-active 仅匹配 /my。调试时用地址栏切换二级路由,审查 class,比死记文字更可靠。
问题:默认高亮类名太长,样式选择器不好写
为什么需要:router-link 在匹配路由时会自动加上高亮类名,方便我们给当前导航项做选中样式。但默认的两个高亮类名比较长,写 CSS 选择器时不够简洁,也不利于和项目里已有的类名规范统一。笔记里给出的问题就是:router-link 的两个高亮类名太长了,希望能定制。

怎么想:既然类名是 router-link 自动加的,就不要去每个链接上手动改,而应该从路由实例的全局配置入手。Vue Router 在创建实例时允许我们额外配置两个选项:linkActiveClass 和 linkExactActiveClass,分别对应“模糊匹配高亮”和“精确匹配高亮”的类名。这样改一次配置,所有 router-link 都生效。
怎么做:在 new VueRouter({...}) 的配置对象中,和 routes 平级地写这两个配置项:
js
const router = new VueRouter({
routes: [...],
linkActiveClass: "类名1",
linkExactActiveClass: "类名2"
})易错点:这两个配置项要写在 VueRouter 的配置对象里,不是写在某一条路由规则里;不要误写成 routes 数组中的某个字段。另外两个名字容易混:linkActiveClass 对应模糊匹配,linkExactActiveClass 对应精确匹配,不要写反。配置完成后,模板里的 router-link 不需要再手动加这个类名。

代码演示:把长类名改成短类名
为什么需要:只知道了配置项还不够,实际项目里通常会把默认长类名改成短、语义清晰的类名,例如 active 和 exact-active,这样 CSS 写起来更直接。
怎么想:笔记中的演示思路是:创建路由对象时,在 routes 后面补上两个配置项。linkActiveClass 配成模糊匹配使用的类名,linkExactActiveClass 配成精确匹配使用的类名。这样页面中对应链接元素上的 class 就会变成我们配置的值。
怎么做:保留笔记中的代码演示:
js
// 创建了一个路由对象
const router = new VueRouter({
routes: [
...
],
linkActiveClass: 'active', // 配置模糊匹配的类名
linkExactActiveClass: 'exact-active' // 配置精确匹配的类名
})易错点:配置项的值是字符串类名,不要写成变量名却忘了定义;类名不要带点号,例如写 'active',不是 '.active'。改完配置后,原来针对默认长类名写的 CSS 要同步改成新类名,否则样式不会生效。还要注意“模糊匹配”和“精确匹配”的差异:嵌套路由场景下,父级链接可能被模糊匹配高亮,而精确匹配只会在完全一致时高亮,因此两个类名要分别写样式时不要混用。
验证与总结:知道配置位置,按需定制
为什么需要:本节标注为“了解”,说明它不是一个需要大量练习的核心 API,但要知道项目里遇到高亮类名太长时从哪里改。转写稿在今日内容介绍中也把“链接高亮”列为路由相关知识点之一,本节就是其中“自定义类名”的补充。
怎么想:验证时不要只看代码有没有报错,而要看渲染后的链接元素上是否出现了配置的类名。配置生效后,router-link 生成的高亮 class 应该由默认长类名变为 active、exact-active 这类自定义值。
怎么做:修改路由配置后重新渲染页面,检查当前导航链接的 class 是否变为配置值;再把 CSS 中的选择器改成新类名即可。总结一句话:自定义 router-link 两个高亮类名,就是在创建 VueRouter 实例时配置 linkActiveClass 和 linkExactActiveClass。
易错点:不要把它和“给 router-link 手动加 class”混为一谈;手动加的 class 是固定类名,而这里配置的是路由匹配时自动切换的高亮类名。配置项拼写要准确,尤其是 Exact 的大小写。最后,两个类名分别对应模糊和精确匹配,写 CSS 时要根据导航层级决定用哪一个,避免父级菜单和子级菜单高亮冲突。
目标:跳转时把数据带过去
为什么需要:搜索页点击“热门搜索”后要跳到详情/搜索页,目标页必须知道用户点的是“黑马程序员”还是“前端培训”,否则只能显示同一个页面。怎么想:把要传的数据放进跳转地址,目标组件再从路由里读出来。怎么做:用声明式导航 <router-link> 跳转时,在 to 里拼参数。易错点:只写目标路径、不拼参数,目标页无法区分点击项。

查询参数传参:地址栏拼 ?参数名=值
为什么需要:查询参数适合传“搜索关键字”这类可选、可组合的数据。怎么想:跳转地址写成 /path?参数名=值,多个参数用 & 连接;路由匹配时主要看路径部分,? 后是查询串。怎么做:<router-link to="/path?参数名=值"></router-link>。易错点:? 前必须是目标路径;参数名和值要写完整;to 不要写成空值。
接收参数:在目标组件里读 query
为什么需要:地址带过去后,组件要取出来渲染。怎么想:组件实例上有当前路由对象,查询参数在 query 里。怎么做:笔记写“固定用法:$router.query.参数名”;在组件脚本中应理解为取当前路由对象,通常写 this.$route.query.参数名,模板中写 $route.query.参数名。易错点:$router 是路由器实例,$route 是当前路由对象;接收要放在能拿到组件实例的位置,笔记示例放在 created 中。转写稿讲路由运行时也强调:点击超链接先改地址栏,再按规则匹配,最后在路由出口展示组件;查询参数只是地址的一部分,组件展示后再读 query。
代码演示(保留笔记代码)
App.vue
vue
<template>
<div id="app">
<div class="link">
<router-link to="/home">首页</router-link>
<router-link to="/search">搜索页</router-link>
</div>
<router-view></router-view>
</div>
</template>
<script>
export default {};
</script>
<style scoped>
.link {
height: 50px;
line-height: 50px;
background-color: #495150;
display: flex;
margin: -8px -8px 0 -8px;
margin-bottom: 50px;
}
.link a {
display: block;
text-decoration: none;
background-color: #ad2a26;
width: 100px;
text-align: center;
margin-right: 5px;
color: #fff;
border-radius: 5px;
}
</style>Home.vue
vue
<template>
<div class="home">
<div class="logo-box"></div>
<div class="search-box">
<input type="text">
<button>搜索一下</button>
</div>
<div class="hot-link">
热门搜索:
<router-link to="">黑马程序员</router-link>
<router-link to="">前端培训</router-link>
<router-link to="">如何成为前端大牛</router-link>
</div>
</div>
</template>
<script>
export default {
name: 'FindMusic'
}
</script>
<style>
.logo-box {
height: 150px;
background: url('@/assets/logo.jpeg') no-repeat center;
}
.search-box {
display: flex;
justify-content: center;
}
.search-box input {
width: 400px;
height: 30px;
line-height: 30px;
border: 2px solid #c4c7ce;
border-radius: 4px 0 0 4px;
outline: none;
}
.search-box input:focus {
border: 2px solid #ad2a26;
}
.search-box button {
width: 100px;
height: 36px;
border: none;
background-color: #ad2a26;
color: #fff;
position: relative;
left: -2px;
border-radius: 0 4px 4px 0;
}
.hot-link {
width: 508px;
height: 60px;
line-height: 60px;
margin: 0 auto;
}
.hot-link a {
margin: 0 5px;
}
</style>Search.vue
vue
<template>
<div class="search">
<p>搜索关键字: 黑马程序员</p>
<p>搜索结果: </p>
<ul>
<li>.............</li>
<li>.............</li>
<li>.............</li>
<li>.............</li>
</ul>
</div>
</template>
<script>
export default {
name: 'MyFriend',
created () {
// 在created中,获取路由参数
}
}
</script>
<style>
.search {
width: 400px;
height: 240px;
padding: 0 20px;
margin: 0 auto;
border: 2px solid #c4c7ce;
border-radius: 5px;
}
</style>router/index.js
js
import Home from '@/views/Home'
import Search from '@/views/Search'
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter) // VueRouter插件初始化
// 创建了一个路由对象
const router = new VueRouter({
routes: [
{ path: '/home', component: Home },
{ path: '/search', component: Search }
]
})
export default routermain.js
...
import router from './router/index'
...
new Vue({
render: h => h(App),
router
}).$mount('#app')练习时的关键改动
为什么需要:上面 Home.vue 中热门搜索的 to 还是空值,只是待完成状态。怎么想:点击哪一项,就把该项内容拼到 /search?参数名=值。怎么做:例如给“黑马程序员”写 to="/search?keyword=黑马程序员",其余链接换成对应文字;然后在 Search.vue 的 created 中用 this.$route.query.keyword 读取。易错点:路由规则路径仍是 /search,查询串不写进 routes 的 path;参数名要前后一致,keyword 不能这里写一个、那里写一个。
一、为什么需要动态路由传参
为什么需要:声明式导航跳转时,经常要把一个值带到目标页面,比如从列表页点进详情页,需要把当前条目的标识传过去。只靠固定路径无法表达“同一个页面组件,不同参数展示不同内容”。
怎么想:先判断要传几个参数。如果只传一个核心参数,比如搜索关键字、商品 id,就可以把参数写进路径本身,让地址看起来像 /search/手机,这就是动态路由传参。它的思路是:路径中留一个“参数位”,跳转时把实际值填进去,目标组件再从当前路由对象里取出来。
怎么做:先配置动态路由,再配置导航链接,最后在组件中接收参数。笔记中的配置如下:
js
const router = new VueRouter({
routes: [
...,
{
path: '/search/:words',
component: Search
}
]
})这里的 :words 就是动态参数位。后面的参数名可以随便起,但要有语义,比如 words 表示搜索关键字,不要起成无意义的 a、b。
易错点:动态路由参数名不是随便写写就完了,它要和接收端保持一致。路径里写 :words,组件里就要取 $route.params.words。另外,跳转地址必须能匹配规则。转写稿中反复调试过:点击超链接后先改变地址栏,再拿地址和规则匹配,匹配成功才在路由出口渲染组件;如果 to 写错,地址和规则对不上,页面就会空白。
二、配置导航链接与接收参数
为什么需要:规则只告诉路由器“什么地址对应什么组件”,真正触发跳转还要靠声明式导航链接。跳转后,组件必须知道当前参数值,才能展示对应内容。
怎么想:动态路由的跳转写法不是查询字符串,而是把参数值直接拼到路径后面:to="/path/参数值"。到了目标组件,当前路由信息挂在 $route 上,动态路径参数放在 params 里。
怎么做:导航链接写成:
html
<router-link to="/search/手机">搜索手机</router-link>对应页面组件接收参数:
js
this.$route.params.words模板中也可以直接写 $route.params.words。
易错点:params 后面的参数名要和动态路由配置的参数保持一致。路径配置是 /search/:words,接收时却写 $route.params.id,就拿不到值。转写稿中讲路由规则时也强调过:to 的地址要和规则里的 path 一致,斜线、单词都不能错;动态路由同样如此,to="/search/手机" 才能匹配 /search/:words。
三、查询参数传参 VS 动态路由传参
为什么需要:传参不止一种方式,选错方式会让地址和代码都别扭。查询参数适合传多个参数,动态路由适合传单个参数,地址更简洁。
怎么想:如果要传“关键字、页码、排序”等多个值,用查询参数更自然;如果只是传一个标识,比如搜索词、详情 id,用动态路由更优雅。
怎么做:查询参数传参的跳转是:
html
<router-link to="/path?参数名=值&参数名2=值">跳转</router-link>获取方式是:
js
this.$route.query.参数名动态路由传参则是:
js
// 配置动态路由
path: "/path/:参数名"
// 跳转
to="/path/参数值"
// 获取
this.$route.params.参数名易错点:不要把两种取值混用。动态路由用 $route.params,查询参数用 $route.query。动态路由也可以传多个参数,但一般只传一个;参数一多,路径会变长,可读性和维护性都不如查询参数。转写稿中讲嵌套路由时提到一级、二级路径会自动拼接,这也提醒我们:动态路由的 to 地址要写完整可匹配路径,不要只写参数值,也不要漏掉前面的路径段。
四、总结:声明式导航跳转时怎么传值
为什么需要:实际开发中要先形成选择标准,避免每个页面都临时决定传参方式。
怎么想:先看参数数量和语义。多个参数、可选参数多,用查询参数;单个核心参数、希望地址简洁,用动态路由。
怎么做:记住两条路线:
- 查询参数传参:
to="/path?参数名=值&参数名2=值",获取$route.query.参数名,比较适合传多个参数。 - 动态路由传参:配置
path: "/path/:参数名",跳转to="/path/参数值",获取$route.params.参数名,优雅简洁,传单个参数比较方便。
易错点:配置、跳转、接收三处要能对上。动态路由中 path 的参数名、to 的实际路径、组件里的 $route.params.参数名 任何一处不一致,都会导致取不到值或匹配不到组件。转写稿中调试路由时已经验证过:地址改变后要和规则匹配,匹配成功才在路由出口展示组件;所以写动态路由时,先把路径规则写对,再写 to,最后写接收,按这个顺序检查最稳。
问题复现:为什么 /search/:words 不传参数会空白
先看本节要解决的问题:路由规则写成 path: "/search/:words",然后按截图中的步骤操作,结果没有匹配到组件,页面显示空白。

这里要先想清楚路由匹配的基本过程。结合转写稿中“小试牛刀”的调试过程:点击超链接后,第一步是地址栏发生变化;第二步是拿当前地址去和 routes 里的规则逐条匹配;第三步是匹配成功后,在 router-view 这个路由出口位置展示对应组件。转写稿里也提到,如果地址和规则“都不一致”,那就“不显示任何空”,页面会一片空白。因此,遇到空白不要先怀疑组件没写,而要先检查:当前地址到底有没有匹配到某条路由规则。
/search/:words 中的 :words 是动态路由参数,表示这一整段必须有值。也就是说,它能匹配 /search/abc、/search/123 这类地址,但不匹配单独的 /search。当你不传参数访问 /search 时,地址和规则对不上,router-view 位置自然没有组件可渲染,于是显示空白。
原因分析:动态参数默认是“必填段”
为什么需要可选符?因为真实开发中,同一个页面可能既支持“带参数访问”,也支持“不带参数访问”。例如搜索页 /search 可以先展示默认内容,用户输入关键词后再变成 /search/手机。如果规则写死为 /search/:words,那么 /search 就被判定为不匹配。
怎么想?把 :words 理解成路径中的一段占位符,而不是查询字符串。它不是“可有可无的附加信息”,默认情况下它是必填的动态段。只有在这一段后面加上可选符 ?,Vue Router 才允许这一段省略。
怎么做?在参数名后面加 ?:
js
const router = new VueRouter({
routes: [
// ...
{ path: '/search/:words?', component: Search }
]
})这样写后,/search 可以匹配,/search/手机 也可以匹配。调试时可以按转写稿中的思路验证:先看地址栏是否变成目标地址,再看它能否和某条 path 规则一致,最后看 router-view 是否出现在正确位置。若 /search 空白,而 /search/abc 正常,就说明问题集中在动态参数是否必填上。
可选符怎么写:位置和写法都要准确
为什么需要强调写法?因为可选符不是随便加在 path 后面,而是加在具体动态参数名后面。/search/:words? 表示 :words 这一段可选;如果位置写错,匹配语义就会变化。
怎么想?先确认哪一段是动态参数,再决定这一段是否允许省略。本节中动态段是 :words,所以 ? 紧跟在 words 后面。
怎么做?保留原有规则,只改 path:
js
{ path: '/search/:words?', component: Search }易错点主要有三类。第一,把 ? 写到错误位置,例如写成 /search?/:words,这不是“让 words 可选”的写法。第二,误以为 ? 可以随便省略或添加,实际上它改变的是匹配规则,必须和访问地址对应。第三,只改了规则却忘了检查 router-view 的位置。转写稿中反复强调“你要在哪里展示组件,就在哪里放 router-view”,如果出口没放对,即使路由匹配成功也可能看不到内容。
验证与调试:用地址变化定位问题
为什么需要调试?因为空白可能来自多个环节:地址没变、规则没匹配、组件没导入、出口没放对。转写稿中的排查顺序很适合这里:点击或访问地址后,先看地址栏;再拿地址和 routes 中的 path 对照;最后看组件是否在路由出口渲染。
怎么做?可以分别访问 /search 和 /search/abc 观察结果。若加 ? 前 /search 空白、/search/abc 正常,加 ? 后 /search 也能显示 Search 组件,就说明可选符生效。若仍然空白,则继续检查 component: Search 是否已正确导入、router-view 是否放在期望展示的位置。
易错点:不要把“路由参数可选”理解成“所有路由都可有可无”;可选符只对当前动态段生效。也不要把地址中的斜线、参数名拼错。转写稿提到地址和规则必须一致,少写字母、斜线开头不一致都会导致匹配失败。总之,本节记住一句话:/search/:words 默认必须传参数;希望 /search 也能匹配,就写成 /search/:words?。
课件图示








代码示例
代码一:router-link 声明式导航 + 两个高亮类名
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>01 声明式导航:router-link 与两个高亮类名</title>
<style>
body { margin: 0; }
.footer_wrap {
display: flex;
height: 50px;
line-height: 50px;
background-color: #495150;
padding-left: 5px;
}
.footer_wrap a {
display: block;
width: 100px;
text-align: center;
margin-right: 5px;
color: #fff;
text-decoration: none;
background-color: #ad2a26;
border-radius: 5px;
}
/* 模糊匹配:to="/my" 能匹配 /my、/my/a、/my/b ... */
.footer_wrap a.router-link-active {
background-color: #0a84ff;
}
/* 精确匹配:to="/my" 只能匹配 /my */
.footer_wrap a.router-link-exact-active {
border-bottom: 4px solid #ffd700;
}
.top { padding: 20px; font-size: 20px; }
</style>
</head>
<body>
<div id="app">
<div class="footer_wrap">
<router-link to="/find">发现音乐</router-link>
<router-link to="/my">我的音乐</router-link>
<router-link to="/my/a">我的音乐-二级</router-link>
<router-link to="/friend">朋友</router-link>
</div>
<div class="top">
<!-- 路由出口 → 匹配的组件所展示的位置 -->
<router-view></router-view>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue-router@3/dist/vue-router.js"></script>
<script>
const Find = { template: '<div>发现音乐组件</div>' }
const My = { template: '<div>我的音乐组件</div>' }
const MyA = { template: '<div>我的音乐 - 二级路由组件</div>' }
const Friend = { template: '<div>朋友组件</div>' }
// 创建了一个路由对象
const router = new VueRouter({
routes: [
{ path: '/find', component: Find },
{ path: '/my', component: My },
{ path: '/my/a', component: MyA },
{ path: '/friend', component: Friend }
]
})
new Vue({
el: '#app',
router
})
</script>
</body>
</html>关键行 / 易错点
<router-link to="...">本质还是 a 标签,但to后面不需要写#,写的是路由规则里path的值(如/find)。router-link的能跳转 + 能高亮是它取代<a>的原因:用<a href="#/find">得自己手动加/删样式,用router-link高亮类名自动切换。- 点“我的音乐-二级”(地址
/my/a)时观察两个类名:to="/my"的链接拿到router-link-active(模糊匹配) —— 只要路径以/my开头就命中;to="/my/a"的链接同时拿到router-link-active+router-link-exact-active(精确匹配)。
- 高亮样式必须写足选择器权重:
.footer_wrap a会盖掉单独的.router-link-active,所以要写成.footer_wrap a.router-link-active。 - 本段为了单独演示“两个类名”,把
/my/a写成了平级规则;真实项目里二级页面通常用children嵌套路由配置(下一节内容),但类名的匹配规则完全一样。 - 忘了写
<router-view>是最常见的“点了没反应/下面一片空白”原因——匹配到的组件没有出口可放。
代码二:自定义高亮类名 + 查询参数传参
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>02 自定义类名 + 查询参数传参</title>
<style>
body { margin: 0; }
.link {
display: flex;
height: 50px;
line-height: 50px;
background-color: #495150;
padding-left: 5px;
}
.link a {
display: block;
width: 100px;
text-align: center;
margin-right: 5px;
color: #fff;
text-decoration: none;
background-color: #ad2a26;
border-radius: 5px;
}
/* 配置了自定义类名后,默认的 router-link-active 就不再加了,样式要写新的类名 */
.link a.active { background-color: #0a84ff; }
.link a.exact-active { border-bottom: 4px solid #ffd700; }
.home { width: 508px; margin: 40px auto; }
.search-box { display: flex; justify-content: center; }
.search-box input {
width: 400px; height: 30px; line-height: 30px;
border: 2px solid #c4c7ce; border-radius: 4px 0 0 4px; outline: none;
}
.search-box input:focus { border: 2px solid #ad2a26; }
.search-box button {
width: 100px; height: 36px; border: none;
background-color: #ad2a26; color: #fff;
position: relative; left: -2px; border-radius: 0 4px 4px 0;
}
.hot-link { width: 508px; height: 60px; line-height: 60px; margin: 0 auto; }
.hot-link a { margin: 0 5px; }
.search {
width: 400px; height: 240px; padding: 0 20px; margin: 40px auto;
border: 2px solid #c4c7ce; border-radius: 5px;
}
</style>
</head>
<body>
<div id="app">
<div class="link">
<router-link to="/home">首页</router-link>
<router-link to="/search">搜索页</router-link>
</div>
<router-view></router-view>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue-router@3/dist/vue-router.js"></script>
<script>
const Home = {
template: `
<div class="home">
<div class="search-box">
<input type="text">
<button>搜索一下</button>
</div>
<div class="hot-link">
热门搜索:
<router-link to="/search?words=黑马程序员">黑马程序员</router-link>
<router-link to="/search?words=前端培训">前端培训</router-link>
<router-link to="/search?words=如何成为前端大牛">如何成为前端大牛</router-link>
</div>
</div>
`
}
const Search = {
template: `
<div class="search">
<p>搜索关键字: {{ $route.query.words }}</p>
<p>搜索结果: </p>
<ul>
<li>.............</li>
<li>.............</li>
<li>.............</li>
<li>.............</li>
</ul>
</div>
`,
created () {
// 在 created 中,获取路由参数
console.log('接收到的查询参数 words =', this.$route.query.words)
}
}
// 创建了一个路由对象
const router = new VueRouter({
routes: [
{ path: '/home', component: Home },
{ path: '/search', component: Search }
],
linkActiveClass: 'active', // 配置模糊匹配的类名
linkExactActiveClass: 'exact-active' // 配置精确匹配的类名
})
new Vue({
el: '#app',
router
})
</script>
</body>
</html>关键行 / 易错点
- 自定义类名只在
new VueRouter({ ... })里多写两项:linkActiveClass、linkExactActiveClass,规则对象不用动。 - 最容易踩的坑:配了自定义类名后,默认的
router-link-active/router-link-exact-active就不再添加了,CSS 必须跟着改成.active/.exact-active,否则看起来“高亮失效”。 - 传参写法:
to="/path?参数名=值";多个参数用&连接:to="/search?words=xx&page=1"。 - 接收写法是
$route.query.参数名(注意是$route不是$router)。$router是路由器实例(做跳转用),$route是当前路由信息对象(取参数用),这个字母差一撇是最常见的报错来源。 - 地址栏里中文会显示成
%E9%BB%91...这种编码,但$route.query.words取到的是正常中文,不用自己解码。 - 查询参数不影响
router-link-active的判断:点“黑马程序员”跳到/search?words=...时,顶部“搜索页”照样高亮。
代码三:动态路由传参 + 可选符 ?
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>03 动态路由传参 + 可选符 ?</title>
<style>
body { margin: 0; }
.link {
display: flex;
height: 50px;
line-height: 50px;
background-color: #495150;
padding-left: 5px;
}
.link a {
display: block;
width: 100px;
text-align: center;
margin-right: 5px;
color: #fff;
text-decoration: none;
background-color: #ad2a26;
border-radius: 5px;
}
.link a.router-link-active { background-color: #0a84ff; }
.home { width: 508px; margin: 40px auto; }
.hot-link { width: 508px; height: 60px; line-height: 60px; margin: 0 auto; }
.hot-link a { margin: 0 5px; }
.search {
width: 400px; height: 240px; padding: 0 20px; margin: 40px auto;
border: 2px solid #c4c7ce; border-radius: 5px;
}
</style>
</head>
<body>
<div id="app">
<div class="link">
<router-link to="/home">首页</router-link>
<router-link to="/search">搜索页(不传参)</router-link>
</div>
<router-view></router-view>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue-router@3/dist/vue-router.js"></script>
<script>
const Home = {
template: `
<div class="home">
<div class="hot-link">
热门搜索:
<router-link to="/search/黑马程序员">黑马程序员</router-link>
<router-link to="/search/前端培训">前端培训</router-link>
<router-link to="/search/如何成为前端大牛">如何成为前端大牛</router-link>
</div>
</div>
`
}
const Search = {
template: `
<div class="search">
<p>搜索关键字: {{ $route.params.words }}</p>
<p>搜索结果: </p>
<ul>
<li>.............</li>
<li>.............</li>
<li>.............</li>
<li>.............</li>
</ul>
</div>
`,
created () {
// 在 created 中,获取路由参数
console.log('接收到的动态路由参数 words =', this.$route.params.words)
}
}
// 创建了一个路由对象
const router = new VueRouter({
routes: [
{ path: '/home', component: Home },
// :words? 里的 ? 是可选符,不加 ? 时 /search 匹配不到组件、页面空白
{ path: '/search/:words?', component: Search }
]
})
new Vue({
el: '#app',
router
})
</script>
</body>
</html>关键行 / 易错点
- 动态路由三件套必须三处名字一致:规则 `path: '/search/:
常见错误
点击导航后地址栏变了,但页面一片空白
- 现象:
<router-link to="/discover">点完 URL 变成/discover,下面什么组件都不渲染,控制台也不报错。 - 原因:
to的值和路由规则里path的值没对上——少字母(/discover写成/discove)、多字母、大小写不同,或一边斜线开头一边没有(path: 'discover'vsto="/discover")。地址变更是成功的,但匹配不到任何规则,自然没有组件可渲染。 - 修法:把
path的值复制粘贴到to里,别手敲;保证两边都斜线开头、拼写完全一致。排错时先看地址栏的哈希值,再逐字比对routes数组。
- 现象:
子路由的
to只写了子路径,点了跳不动- 现象:嵌套路由里写
<router-link to="/toplist">或to="toplist",点击后页面空白或直接跳到别处。 - 原因:子路由的实际访问地址是父路径 + 子路径拼接出来的(
/discover/toplist),单独写子段永远匹配不到。 - 修法:
to必须写拼接后的完整地址/discover/toplist;或者在子路由规则里把path写成斜线开头的绝对路径(但这样父组件会一起被替换掉,通常不是想要的)。
- 现象:嵌套路由里写
<router-view>放错位置,组件"没出来"- 现象:路由配好了、链接也点得动、地址也变了,就是下面没内容;或者把
<router-view>写进了某个没被用到的组件里,页面毫无反应。 - 原因:
router-view是"占位符",只在它所在的那个组件模板里渲染匹配到的组件。一级路由的组件只能渲染在根组件App.vue中,放到别的组件里不会生效;子路由的router-view必须写在父路由组件(如Discover.vue)内部,写到App.vue或别的组件里同样无效。 - 修法:先想清楚"这个组件是几级路由",一级 → 出口放
App.vue;二级 → 出口放对应父组件里。放之前确认那个组件确实被当前路由渲染出来了。
- 现象:路由配好了、链接也点得动、地址也变了,就是下面没内容;或者把
$router.query/$route.query混用,参数取到undefined- 现象:跳转链接写的是
to="/search?keyword=黑马",接收页created里打印是undefined。 - 原因:
$router是路由实例(用于push/replace等方法),$route是当前路由信息对象(才有query、params、path)。写成$router.query一定拿不到。 - 修法:取参统一用
this.$route.query.参数名/this.$route.params.参数名;跳转动作才用this.$router.push(...)。记法:带r的$router干活,不带的$route取数据。
- 现象:跳转链接写的是
动态路由参数名对不上,
params永远是undefined- 现象:
path: '/search/:words',组件里写this.$route.params.keyword,取不到值。 - 原因:
params的键名必须和path中:后面的名字逐字一致,改一边不改另一边就失效;另外不带参数访问/search时因为没配可选符?,会直接匹配失败显示空白。 - 修法:
path: '/search/:words'↔$route.params.words;需要允许不传参访问时写path: '/search/:words?'。
- 现象:
高亮样式加错类名,多个导航同时亮
- 现象:进入
/discover/toplist后,"发现音乐"和它的子菜单同时高亮,或者只想让精确匹配的那一项亮却做不到。 - 原因:
router-link-active是模糊匹配(前缀匹配),/discover/toplist会同时命中to="/discover";router-link-exact-active才是精确匹配。样式加在了前者上就必然多亮。 - 修法:只想要唯一高亮就用
.router-link-exact-active;类名太长可在new VueRouter({ linkActiveClass: 'active', linkExactActiveClass: 'exact-active' })中改名后再写样式。
- 现象:进入
练习
排错题:给定
path: '/my'和<router-link to="my">我的音乐</router-link>,点击后页面空白。请指出所有导致空白的原因并逐个改正(至少找出 2 处),改完后验证地址栏哈希值。类名验证题:搭一个
App.vue+Discover.vue+ 两个子页面(推荐 / 排行榜)的嵌套路由。进入/discover/toplist后,用浏览器开发者工具分别记录:哪些router-link带有router-link-active、哪些带有router-link-exact-active。然后把两个类名分别换成active/exact-active再记录一次,写下一句结论说明二者差别。传参对比题:从首页的"热门搜索"跳到搜索页,把关键词带过去。要求用两种方式各实现一遍:① 查询参数
to="/search?keyword=黑马程序员",接收端$route.query.keyword;② 动态路由path: '/search/:words',接收端$route.params.words。两版都跑通后,回答:如果关键词里带空格或&,哪种方式更省心?可选符实验题:先把
path配成/search/:words,直接在地址栏输入http://localhost:8080/#/search回车,观察结果;再加可选符改成/search/:words?,重复同样操作。把两次的现象和控制台/页面表现写下来,并说明什么时候需要加?。位置判断题:故意把子路由的
<router-view>从Discover.vue挪到App.vue中(保留原来的那个),点击"排行榜"观察现象,再把App.vue里的删掉恢复正确写法。用一句话解释"占位符必须放在哪"的判断依据。
原视频对照
| # | 视频 | 时长 |
|---|---|---|
| 1 | 回顾sync修饰符 | 19:07 |
| 2 | 回顾loading指令 | 16:50 |
| 3 | 今日内容介绍 | 04:16 |
| 4 | 路由介绍 | 10:39 |
| 5 | 路由插件安装配置 | 12:20 |
| 6 | 小试牛刀-初步使用路由插件 | 13:57 |
| 7 | 小结路由使用问题 | 08:11 |
| 8 | 嵌套路由 | 20:00 |
小结
router-link的价值不在"能跳",而在"跳完自动知道谁是当前项"——省掉手写"加一个类、删一个类"的活,这是它相对<a>的唯一实质优势。- 声明式导航的排错路径永远是固定的三段:地址栏的哈希值 →
routes里的path→ 页面上router-view的位置。空白页 90% 卡在第一段和第二段对不上,剩下 10% 是出口放错。 router-view不是"在哪写都行"的装饰,它和路由层级是绑死的:几级路由,出口就写在几级的父组件里。这条规矩决定了你改需求时该动哪个文件。$route与$router一个是"数据"一个是"动作",混用是新手最高频的隐性错误——它不报错,只默默返回undefined。- 两种传参不是"二选一"的知识点,而是按场景选工具:参数多、可有可无 →
query(URL 上可见、可刷新保留);单个语义化参数、想要干净 URL →params。选错的代价是后期改路径结构时牵连一片。 - 模糊匹配是
router-link-active的默认行为,不是 bug。遇到"多亮一个"先想匹配模式,再想是不是该换exact,不要急着用!important盖样式。 - 路由配置里"组件怎么嵌套,规则就怎么嵌套"——
children的结构本质是组件树结构在配置上的镜像。先画组件嵌套图,再写routes,比边写边试快得多。 - 这一节的三个核心步骤(配规则 → 放出口 → 写链接)会原样复用到后面所有路由场景,包括项目里的导航守卫和权限路由。步骤本身不变,变的只是每一步里塞什么。