Skip to content

路由进阶(一) ​

本页对应 路由进阶,共 8 段视频,约 1 小时 45 分。

学习目标 ​

  1. 能使用 router-link 编写声明式导航链接,并验证 to 属性与路由规则 path 的对应关系。
  2. 能区分 router-link-active 与 router-link-exact-active 两个类名,并通过切换路由验证其激活匹配差异。
  3. 能通过 linkActiveClass、linkExactActiveClass 配置自定义链接高亮类名,并验证样式生效。
  4. 能使用查询参数传参,在目标组件中通过 $route.query 读取参数并正确渲染。
  5. 能配置动态路由参数,在目标组件中通过 $route.params 获取参数并展示对应内容。
  6. 能使用动态路由参数可选符 ? 配置可选参数,并验证有参、无参两种访问场景。

知识点 ​

需求场景:为什么需要声明式导航 ​

做导航高亮时,如果直接用 a 标签,点击后虽然能跳转,但我们要手动给当前链接加样式,还要移除上一个链接的样式。链接一多,维护成本立刻上来,容易漏改、错改。笔记里的效果就是:当前在哪个导航项,哪个导航项就高亮。

导航高亮效果示例

所以这里的核心思路不是“自己管理高亮”,而是把跳转和选中状态交给 vue-router 提供的全局组件 router-link。它替代 a 标签,既能跳转,又能默认提供高亮类名。

为什么需要:路由页面切换需要改变哈希地址,地址变化后再匹配路由规则,最后在 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 是模糊匹配(用的多)。

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 是精确匹配。

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默认高亮类名过长示意

怎么想:既然类名是 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 router

main.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?。

课件图示 ​

课件第2页:目录 Contents u 路由进阶 ① 路由模块封装 ② 声明式导航 & 导航

课件第18页:1. 查询参数传参 2. 动态路由传参 声明式导航 - 跳转传参 目标:在跳转路

课件第22页:总结 小结 声明式导航跳转时, 有几种方式传值给路由页面? ① 查询参数传参 (

课件第20页:2. 动态路由传参 ① 配置动态路由 ② 配置导航链接 l to="/path/

课件第48页:总结 总结 1. 项目案例实现的基本步骤分哪两大步? ① 配路由 ② 实现页面功

课件第19页:1. 查询参数传参 ① 语法格式如下 l to="/path?参数名=值" ②

课件第12页:① router-link-active 模糊匹配 (用的多) to="/my"

课件第15页:声明式导航 - 两个类名 说明: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/:

常见错误 ​

  1. 点击导航后地址栏变了,但页面一片空白

    • 现象:<router-link to="/discover"> 点完 URL 变成 /discover,下面什么组件都不渲染,控制台也不报错。
    • 原因:to 的值和路由规则里 path 的值没对上——少字母(/discover 写成 /discove)、多字母、大小写不同,或一边斜线开头一边没有(path: 'discover' vs to="/discover")。地址变更是成功的,但匹配不到任何规则,自然没有组件可渲染。
    • 修法:把 path 的值复制粘贴到 to 里,别手敲;保证两边都斜线开头、拼写完全一致。排错时先看地址栏的哈希值,再逐字比对 routes 数组。
  2. 子路由的 to 只写了子路径,点了跳不动

    • 现象:嵌套路由里写 <router-link to="/toplist"> 或 to="toplist",点击后页面空白或直接跳到别处。
    • 原因:子路由的实际访问地址是父路径 + 子路径拼接出来的(/discover/toplist),单独写子段永远匹配不到。
    • 修法:to 必须写拼接后的完整地址 /discover/toplist;或者在子路由规则里把 path 写成斜线开头的绝对路径(但这样父组件会一起被替换掉,通常不是想要的)。
  3. <router-view> 放错位置,组件"没出来"

    • 现象:路由配好了、链接也点得动、地址也变了,就是下面没内容;或者把 <router-view> 写进了某个没被用到的组件里,页面毫无反应。
    • 原因:router-view 是"占位符",只在它所在的那个组件模板里渲染匹配到的组件。一级路由的组件只能渲染在根组件 App.vue 中,放到别的组件里不会生效;子路由的 router-view 必须写在父路由组件(如 Discover.vue)内部,写到 App.vue 或别的组件里同样无效。
    • 修法:先想清楚"这个组件是几级路由",一级 → 出口放 App.vue;二级 → 出口放对应父组件里。放之前确认那个组件确实被当前路由渲染出来了。
  4. $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 取数据。
  5. 动态路由参数名对不上,params 永远是 undefined

    • 现象:path: '/search/:words',组件里写 this.$route.params.keyword,取不到值。
    • 原因:params 的键名必须和 path 中 : 后面的名字逐字一致,改一边不改另一边就失效;另外不带参数访问 /search 时因为没配可选符 ?,会直接匹配失败显示空白。
    • 修法:path: '/search/:words' ↔ $route.params.words;需要允许不传参访问时写 path: '/search/:words?'。
  6. 高亮样式加错类名,多个导航同时亮

    • 现象:进入 /discover/toplist 后,"发现音乐"和它的子菜单同时高亮,或者只想让精确匹配的那一项亮却做不到。
    • 原因:router-link-active 是模糊匹配(前缀匹配),/discover/toplist 会同时命中 to="/discover";router-link-exact-active 才是精确匹配。样式加在了前者上就必然多亮。
    • 修法:只想要唯一高亮就用 .router-link-exact-active;类名太长可在 new VueRouter({ linkActiveClass: 'active', linkExactActiveClass: 'exact-active' }) 中改名后再写样式。

练习 ​

  1. 排错题:给定 path: '/my' 和 <router-link to="my">我的音乐</router-link>,点击后页面空白。请指出所有导致空白的原因并逐个改正(至少找出 2 处),改完后验证地址栏哈希值。

  2. 类名验证题:搭一个 App.vue + Discover.vue + 两个子页面(推荐 / 排行榜)的嵌套路由。进入 /discover/toplist 后,用浏览器开发者工具分别记录:哪些 router-link 带有 router-link-active、哪些带有 router-link-exact-active。然后把两个类名分别换成 active / exact-active 再记录一次,写下一句结论说明二者差别。

  3. 传参对比题:从首页的"热门搜索"跳到搜索页,把关键词带过去。要求用两种方式各实现一遍:① 查询参数 to="/search?keyword=黑马程序员",接收端 $route.query.keyword;② 动态路由 path: '/search/:words',接收端 $route.params.words。两版都跑通后,回答:如果关键词里带空格或 &,哪种方式更省心?

  4. 可选符实验题:先把 path 配成 /search/:words,直接在地址栏输入 http://localhost:8080/#/search 回车,观察结果;再加可选符改成 /search/:words?,重复同样操作。把两次的现象和控制台/页面表现写下来,并说明什么时候需要加 ?。

  5. 位置判断题:故意把子路由的 <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

小结 ​

  1. router-link 的价值不在"能跳",而在"跳完自动知道谁是当前项"——省掉手写"加一个类、删一个类"的活,这是它相对 <a> 的唯一实质优势。
  2. 声明式导航的排错路径永远是固定的三段:地址栏的哈希值 → routes 里的 path → 页面上 router-view 的位置。空白页 90% 卡在第一段和第二段对不上,剩下 10% 是出口放错。
  3. router-view 不是"在哪写都行"的装饰,它和路由层级是绑死的:几级路由,出口就写在几级的父组件里。这条规矩决定了你改需求时该动哪个文件。
  4. $route 与 $router 一个是"数据"一个是"动作",混用是新手最高频的隐性错误——它不报错,只默默返回 undefined。
  5. 两种传参不是"二选一"的知识点,而是按场景选工具:参数多、可有可无 → query(URL 上可见、可刷新保留);单个语义化参数、想要干净 URL → params。选错的代价是后期改路径结构时牵连一片。
  6. 模糊匹配是 router-link-active 的默认行为,不是 bug。遇到"多亮一个"先想匹配模式,再想是不是该换 exact,不要急着用 !important 盖样式。
  7. 路由配置里"组件怎么嵌套,规则就怎么嵌套"——children 的结构本质是组件树结构在配置上的镜像。先画组件嵌套图,再写 routes,比边写边试快得多。
  8. 这一节的三个核心步骤(配规则 → 放出口 → 写链接)会原样复用到后面所有路由场景,包括项目里的导航守卫和权限路由。步骤本身不变,变的只是每一步里塞什么。