Code前端首页关于Code前端联系我们

做Vue项目时,导航菜单当前页高亮是刚需,但Vue Router的active相关配置咋用?碰到样式不生效、嵌套路由父菜单一直亮这些问题咋解决?这篇从基础用法到复杂场景,把路由激活的门道一次性讲透

terry 3周前 (09-04) 阅读数 48 #Vue

router-link 咋给激活路由加样式?

想让导航项在对应路由激活时自动变高亮,Vue Router的 <router-link> 组件自带“激活状态”处理逻辑,核心是两个属性:active-classexact-active-class,再配合CSS就能实现样式切换。

先看最基础的用法:

<template>
  <nav>
    <!-- 给首页导航加激活样式 -->
    <router-link to="/home" active-class="active-nav">首页</router-link>
    <!-- 给关于页导航加激活样式 -->
    <router-link to="/about" active-class="active-nav">lt;/router-link>
  </nav>
</template>
<style>
.active-nav {
  color: #ff4d4f; /* 高亮颜色 */
  border-bottom: 2px solid #ff4d4f;
}
</style>

当访问 /home 时,<router-link to="/home"> 会自动添加 active-nav 类;切到 /about 时,/about 对应的router-link加类,/home 的类会被移除。

要是不想自定义 active-class,Vue Router还提供了默认类名router-link-active(普通激活)和 router-link-exact-active(精确激活),所以也能直接写CSS:

.router-link-active {
  /* 你的高亮样式 */
}

这里得理解路由匹配规则:Vue Router用 path-to-regexp 库处理路径匹配,默认是“前缀匹配”,比如路由配置是 /home,当前路径是 /home/detail/home 对应的router-link也会被认为是激活状态(因为路径以 /home 开头),如果想精确匹配(只有路径完全一致才激活),得加 exact 属性:

<router-link to="/" exact active-class="active-nav">首页</router-link>

加了 exact 后,只有访问 时才会激活,访问 /home 这类子路径时不会误激活。

坑点1:active样式为啥突然不生效?常见原因排查

碰到active样式没效果,别慌,先从这几个方向排查:

路由匹配规则理解错了

比如路由是 /product,但导航项的router-link写的是 /products(路径拼写错了),或者没考虑前缀匹配导致冲突,举个例子:
路由配置有 和 /about,导航栏两个router-link分别是 和 /about,访问 /about 时, 对应的router-link也会被激活(因为 /about 包含 这个前缀),这时候给 加 exact 就能解决:

<router-link to="/" exact active-class="active-nav">首页</router-link>

CSS优先级被覆盖

如果写了 .active-nav 样式但没生效,先检查是否被更高级的选择器覆盖了,比如UI框架自带的样式权重更高,这时候可以用 !important 临时测试,或者调整选择器权重(比如用 nav .active-nav 代替 .active-nav)。

动态路由参数的“隐性匹配”

动态路由(如 /user/:id)场景下,不同参数的路径会被视为不同路由,但router-link的active匹配是“路径是否匹配”,比如有两个导航项:

<router-link to="/user/1" active-class="active">用户1</router-link>
<router-link to="/user/2" active-class="active">用户2</router-link>

访问 /user/1 时,只有第一个router-link激活;但如果导航项是“用户中心”,想让所有 /user/:id 路径都激活同一个导航项,直接用router-link的 to="/user" 会失效(因为路由配置里可能没有 /user 这个路径,只有 /user/:id),这时候得换思路(后面“动态路由场景”会详细讲)。

嵌套路由下,父菜单一直高亮咋处理?

嵌套路由(比如父路由 /order,子路由 /order/list/order/detail)里,父路由的router-link会因为“路径包含匹配”一直处于active状态——访问子路由时,路径是 /order/list,包含父路由的 /order,所以父导航项的active样式不会消失,这种“父菜单一直亮”的情况,得看需求:

需求1:父菜单仅在访问自身时高亮,子路由时不亮

这时候默认的router-link匹配逻辑满足不了,得手动控制active状态,放弃用router-link的自动active,改成自定义点击事件+路由判断:

<template>
  <div class="nav-item" 
       :class="{ active: $route.path === '/order' }"
       @click="$router.push('/order')"
  >
    订单管理
  </div>
</template>
<style>
.active { color: red; }
</style>

通过 $route.path 精确判断当前路径是否是父路由 /order,只有完全匹配时才加active样式。

需求2:父菜单在访问任意子路由时都高亮

这种场景更常见(比如侧边栏的父菜单,子页面打开时父菜单保持高亮),这时候得利用路由匹配记录 $route.matched

路由配置里,父路由和子路由的关系是“嵌套”的,所以子路由的匹配记录($route.matched)会包含父路由的信息,比如父路由name是 Order,子路由匹配时,$route.matched 数组里能找到name为 Order 的记录。

所以可以这样写:

<template>
  <div class="nav-item" 
       :class="{ active: $route.matched.some(record => record.name === 'Order') }"
       @click="$router.push('/order')"
  >
    订单管理
  </div>
</template>

$route.matched.some(...) 意思是“匹配记录中是否有name为Order的路由”,只要子路由被访问(子路由的matched包含父路由),这个条件就成立,父菜单就会高亮。

动态路由(如/user/1)咋让导航项稳定激活?

动态路由带参数(如 /user/:id)时,router-link的active匹配容易“挑刺”——不同参数的路径算不同路由,导航项想“一视同仁”激活,得换思路:

场景1:单个导航项对应所有动态路由(如“用户中心”包含/user/1、/user/2)

这时候别用router-link的 to 指向具体参数(/user/1),而是判断路径前缀路由名称

方法1:路径前缀判断

<template>
  <div class="nav-item" 
       :class="{ active: $route.path.startsWith('/user/') }"
       @click="$router.push('/user/1')"
  >
    用户中心
  </div>
</template>

只要当前路径以 /user/ 开头(不管id是啥),就激活“用户中心”。

方法2:路由名称匹配
给动态路由配个name(UserDetail),然后判断当前路由name是否是 UserDetail

<template>
  <div class="nav-item" 
       :class="{ active: $route.name === 'UserDetail' }"
       @click="$router.push('/user/1')"
  >
    用户中心
  </div>
</template>
// 路由配置
{ 
  path: '/user/:id', 
  name: 'UserDetail', 
  component: UserDetail 
}

这样不管id是1还是2,只要路由name是 UserDetail,导航项就激活。

UI框架导航组件(如Element UI Menu)咋结合路由active?

很多UI框架的导航组件(如Element UI的 <el-menu>)需要手动设置“当前激活项”,这时候得把Vue Router的active状态和UI组件的激活逻辑绑定。

以Element UI为例,<el-menu> 有个 default-active 属性,用来指定当前激活的菜单项的 index,我们可以把它和当前路由的path/name绑定:

方式1:绑定当前路由path

<template>
  <el-menu :default-active="currentPath" mode="horizontal">
    <el-menu-item index="/home">首页</el-menu-item>
    <el-menu-item index="/about">lt;/el-menu-item>
    <el-menu-item index="/user/1">用户1</el-menu-item>
  </el-menu>
</template>
<script>
export default {
  computed: {
    currentPath() {
      return this.$route.path
    }
  }
}
</script>

访问 /user/1 时,currentPath/user/1,和 <el-menu-item index="/user/1"> 匹配,菜单项就会高亮。

方式2:绑定路由name(解决动态参数匹配问题)

如果动态路由是 /user/:id<el-menu-item>index 没法写 :id(静态属性不支持动态参数),这时候用路由name更灵活:

<template>
  <el-menu :default-active="currentRouteName" mode="horizontal">
    <el-menu-item index="home">首页</el-menu-item>
    <el-menu-item index="about">lt;/el-menu-item>
    <el-menu-item index="userDetail">用户中心</el-menu-item>
  </el-menu>
</template>
<script>
export default {
  computed: {
    currentRouteName() {
      return this.$route.name
    }
  }
}
</script>
// 路由配置
{ 
  path: '/home', 
  name: 'home', 
  component: Home 
},
{ 
  path: '/about', 
  name: 'about', 
  component: About 
},
{ 
  path: '/user/:id', 
  name: 'userDetail', 
  component: UserDetail 
}

这样不管 /user/:id 的id是多少,只要路由name是 userDetail<el-menu-item index="userDetail"> 就会被激活。

自定义active逻辑:不用router-link默认匹配咋做?

有时候默认的active匹配不够灵活(比如多级菜单、多标签页、复杂权限控制场景),这时候得手动控制active状态,核心是利用 $route 对象的属性(pathnamematched 等)判断当前路由是否属于某个导航项的“范围”。

例子:多级侧边栏,父菜单在子路由时高亮

侧边栏有“订单管理”,下面有“全部订单”(/order/list)、“待支付”(/order/unpaid)两个子路由,要求:访问任何子路由时,“订单管理”父菜单都高亮。

步骤:

  1. 给父菜单绑定动态class,判断当前路由是否属于“订单管理”的子路由。
  2. 利用 $route.matched 检查是否包含父路由的匹配记录。

代码实现:

<template>
  <div class="sidebar">
    <!-- 父菜单:订单管理 -->
    <div 
      class="parent-nav"
      :class="{ active: isOrderRoute }"
      @click="$router.push('/order/list')"
    >
      订单管理
    </div>
    <!-- 子菜单 -->
    <div class="child-nav" @click="$router.push('/order/list')">全部订单</div>
    <div class="child-nav" @click="$router.push('/order/unpaid')">待支付</div>
  </div>
</template>
<script>
export default {
  computed: {
    isOrderRoute() {
      // 检查路由匹配记录中是否有name为Order的路由(父路由)
      return this.$route.matched.some(record => record.name === 'Order')
    }
  }
}
</script>
<style>
.parent-nav.active {
  background: #f5f7fa;
  color: #ff4d4f;
}
</style>
// 路由配置
{
  path: '/order',
  name: 'Order',
  component: OrderLayout,
  children: [
    { path: 'list', name: 'OrderList', component: OrderList },
    { path: 'unpaid', name: 'OrderUnpaid', component: OrderUnpaid }
  ]
}

当访问 /order/list/order/unpaid 时,$route.matched 会包含父路由 Order 的记录,isOrderRoute 为true,父菜单“订单管理”就会高亮。

路由active的核心逻辑和避坑思路

Vue Router的active处理,本质是“路由匹配”+“样式绑定”的结合,核心要理解:

  • router-link的默认匹配是前缀匹配,精确匹配需加 exact
  • 复杂场景(嵌套、动态路由)下,默认匹配逻辑不够用,得手动结合$route的path/name/matched判断;
  • 和UI框架结合时,要把Vue Router的路由状态(path/name)和UI组件的激活属性(如Element UI的default-active)绑定。

避坑关键:碰到样式不生效,先检查路由路径是否匹配、CSS优先级、动态路由的隐性匹配问题;嵌套路由和动态路由场景,优先考虑手动控制active状态(用$route的属性判断)。

掌握这些逻辑后,不管是简单的导航栏高亮,还是复杂的多级菜单、权限控制下的路由激活,都能轻松应对~

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门