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

Vue Router 不同版本的基础获取方式

terry 3小时前 阅读数 8 #Vue

做Vue项目时,不少同学碰到权限管理、动态菜单生成这类需求,得先拿到整个项目的路由列表,那Vue Router 到底怎么获取所有路由?这篇文章从基础用法、嵌套路由处理到实际开发场景,一步步把“get all routes”的门道讲透~

首先得分清Vue Router的版本,因为Vue2和Vue3生态下的路由API有差异,获取路由的方式也不一样。

Vue2 + Vue Router 3.x:从 options.routes 入手

在Vue2项目里,我们创建路由实例是用 new VueRouter({ routes }),路由实例挂载后,通过 this.$router.options.routes 能拿到初始化时配置的路由数组,举个简单例子:

// 路由配置文件 router.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from './views/Home.vue'
import About from './views/About.vue'
Vue.use(VueRouter)
const routes = [
  { path: '/', name: 'Home', component: Home },
  { path: '/about', name: 'About', component: About }
]
const router = new VueRouter({ routes })
export default router

在组件里想获取路由列表,就可以这样写:

export default {
  mounted() {
    const allRoutes = this.$router.options.routes
    console.log(allRoutes) // 能拿到初始定义的 Home、About 这两个路由
  }
}

但要注意!options.routes 存的是初始配置,如果用 router.addRoute 动态添加路由,options.routes 里不会自动包含新路由,比如后来加了个 /new 路由,options.routes 里还是只有最初的两个~

Vue3 + Vue Router 4.x:用 getRoutes() 更灵活

Vue3配合的Vue Router 4,创建路由是 createRouter 方法,它的路由实例上有个 getRoutes() 方法,专门用来获取当前所有已注册的路由记录(包括动态添加的),用法更“跟得上变化”:

// 路由配置文件 router.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from './views/Home.vue'
import About from './views/About.vue'
const routes = [
  { path: '/', name: 'Home', component: Home },
  { path: '/about', name: 'About', component: About }
]
const router = createRouter({
  history: createWebHistory(),
  routes
})
// 动态添加新路由
router.addRoute({ path: '/new', name: 'New', component: () => import('./views/New.vue') })
export default router

在组件或其他逻辑里获取路由,直接调用 getRoutes()

import router from './router.js'
const allRoutes = router.getRoutes()
console.log(allRoutes) // 包含初始的 Home、About,还有动态添加的 New

对比下来,Vue3的 getRoutes() 更适合“实时获取所有路由”的场景,尤其是有动态路由添加时,不用自己额外维护路由列表~

处理嵌套路由:递归才能拿全所有子路由

实际项目里,路由很少是“平铺”的,比如后台管理系统,常用嵌套路由做布局:

const routes = [
  {
    path: '/dashboard',
    name: 'Dashboard',
    component: Layout, // 布局组件
    children: [
      { path: 'analysis', name: 'Analysis', component: Analysis },
      { path: 'workbench', name: 'Workbench', component: Workbench }
    ]
  },
  {
    path: '/settings',
    name: 'Settings',
    component: Layout,
    children: [
      { path: 'user', name: 'User', component: User },
      { path: 'role', name: 'Role', component: Role }
    ]
  }
]

这时候如果直接拿 routes 数组,只能拿到一级路由(Dashboard、Settings),子路由(analysis、user这些)藏在 children 里,所以必须用递归遍历,把所有层级的路由都捞出来~

写个通用的递归函数:

// 递归遍历所有路由(Vue2、Vue3通用逻辑)
function getAllRoutesRecursive(routes) {
  let allRoutes = []
  function traverse(routeList) {
    routeList.forEach(route => {
      allRoutes.push(route) // 先把当前路由加入列表
      if (route.children && route.children.length > 0) {
        traverse(route.children) // 有子路由就递归处理
      }
    })
  }
  traverse(routes)
  return allRoutes
}

用法也很简单:

// 以Vue3为例,先拿到所有路由(包括动态添加的)
import router from './router.js'
const runtimeRoutes = router.getRoutes()
const allNestedRoutes = getAllRoutesRecursive(runtimeRoutes)
console.log(allNestedRoutes) // 现在Dashboard、Settings的子路由也被包含啦~

要是用Vue2,就把 router.options.routes 传进去:

const initialRoutes = this.$router.options.routes
const allNestedRoutes = getAllRoutesRecursive(initialRoutes)

这里的关键是“递归”——只要路由里有 children,就继续往下遍历,保证每个层级的路由都被收集到,新手很容易漏掉这一步,导致子路由“消失”,得特别注意~

实际开发中,获取所有路由能解决哪些问题?

拿到所有路由后,能在很多场景下简化开发,举几个常见的例子:

场景1:权限控制,按角色筛路由

后台系统里,不同角色(比如管理员、普通用户)能访问的页面不一样,获取所有路由后,筛出符合角色权限的路由,再动态生成可访问的路由表。

假设路由配置里用 meta.role 标记可访问角色,Vue3项目可以这么做:

// 工具函数:根据角色过滤路由
import { useStore } from 'vuex'
import router from './router.js'
export function filterRoutesByRole() {
  const store = useStore()
  const userRole = store.state.user.role // 假设用户角色存在Vuex里
  const allRoutes = getAllRoutesRecursive(router.getRoutes()) // 先拿到所有路由
  return allRoutes.filter(route => {
    // 如果路由没配meta.role,默认允许访问;否则检查是否包含用户角色
    const allowedRoles = route.meta?.role || []
    return allowedRoles.length === 0 || allowedRoles.includes(userRole)
  })
}
// 导航守卫里判断权限
router.beforeEach((to, from, next) => {
  const accessibleRoutes = filterRoutesByRole()
  // 检查目标路由是否在可访问列表里
  if (accessibleRoutes.some(route => route.path === to.path)) {
    next() // 有权限,放行
  } else {
    next('/403') // 没权限,跳转到403页面
  }
})

场景2:动态生成导航菜单,少写重复代码

中后台系统的侧边栏/顶部菜单,经常要和路由一一对应,手动维护菜单太麻烦,不如用路由自动生成。

比如做个递归组件 MenuNode 渲染菜单,结合路由的 meta.title)、meta.icon(菜单图标)等信息:

<template>
  <aside class="sidebar">
    <ul>
      <MenuNode 
        v-for="route in menuRoutes" 
        :key="route.path" 
        :route="route" 
      />
    </ul>
  </aside>
</template>
<script setup>
import { computed } from 'vue'
import router from './router.js'
import MenuNode from './MenuNode.vue'
// 递归生成菜单需要的结构(过滤掉不需要显示的路由)
function generateMenuRoutes(routes) {
  return routes.map(route => {
    const menuItem = {
      path: route.path,
      name: route.name,
      title: route.meta?.title || route.name, // 优先用meta.title,没有就用路由name
      icon: route.meta?.icon,
      children: route.children ? generateMenuRoutes(route.children) : []
    }
    // 如果路由配置了meta.hidden为true,就不在菜单显示
    if (route.meta?.hidden) return null
    return menuItem
  }).filter(item => item) // 过滤掉null(即hidden为true的路由)
}
// 计算属性:获取所有路由并生成菜单结构
const menuRoutes = computed(() => {
  const allRoutes = getAllRoutesRecursive(router.getRoutes())
  return generateMenuRoutes(allRoutes)
})
</script>

这样一来,只要在路由配置里写好 meta.titlemeta.iconmeta.hidden,菜单就自动生成了,不用手动改菜单组件~

场景3:全局路由埋点,统计页面访问

想统计每个页面的访问次数、停留时间?在路由守卫里结合所有路由信息,就能实现埋点:

router.afterEach((to, from) => {
  const allRoutes = getAllRoutesRecursive(router.getRoutes())
  const currentRoute = allRoutes.find(route => route.path === to.path)
  // 发送埋点数据(这里用trackEvent模拟埋点函数)
  trackEvent('route_visit', {
    routeName: currentRoute.name,
    path: currentRoute.path, currentRoute.meta?.title,
    timestamp: new Date().getTime()
  })
})

通过 afterEach 钩子,每次路由跳转后,拿到目标路由的信息,上报给统计系统,这样产品经理要看页面访问数据时,就有依据啦~

避坑指南:这些细节要注意

获取所有路由时,有些容易踩的坑,提前避坑能少熬夜debug~

分清“初始路由”和“运行时路由”

Vue2里,this.$router.options.routes 只存初始化时的路由,如果用 router.addRoute 动态加路由,options.routes 不会更新!这时候要么自己维护一份路由列表,要么结合其他方式(比如把动态路由也push到options.routes里,但不推荐,容易乱)。

Vue3的 router.getRoutes()运行时所有已注册的路由,动态添加的路由会被包含,所以更推荐用这个~

嵌套路由必须递归遍历

新手常犯的错:直接循环 router.options.routesgetRoutes() 的结果,却忘记处理 children 数组,结果只拿到一级路由,子路由全丢了,所以一定要写递归函数,把所有层级的路由都“挖”出来~

善用路由元信息(meta)

路由的 meta 字段是个“百宝箱”——可以存权限角色、菜单标题、是否隐藏、图标等信息,获取所有路由后,结合 meta 能实现各种灵活逻辑(比如前面的权限控制、菜单生成),写路由配置时,别浪费这个好工具~

动态导入组件不影响路由结构

项目里常用 component: () => import('./views/About.vue') 做代码分割(动态导入组件),这种情况下,路由的 component 是个加载函数,但不影响路由结构的获取,所以递归遍历的时候,不用管组件是不是异步加载的,专心处理路由的层级就行~

核心逻辑+拓展思考

获取Vue Router所有路由的核心逻辑可以总结为:

  • 分版本处理:Vue2用 this.$router.options.routes(拿初始路由),Vue3用 router.getRoutes()(拿运行时所有路由);
  • 嵌套路由要递归:写个递归函数遍历 children,保证所有层级路由都被收集;
  • 结合业务场景:权限控制、动态菜单、埋点统计等场景下,用路由列表实现自动化逻辑,减少重复劳动。

拓展一下:如果是多页应用(不是单页SPA),Vue Router的使用场景会不同,但“获取所有路由”的逻辑类似;把路由列表存在Vuex或Pinia里,在全局状态中统一管理,也是很多团队的实践方式~

版权声明

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

发表评论:

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

热门