Vue Router 不同版本的基础获取方式
做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.title
、meta.icon
、meta.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.routes
或 getRoutes()
的结果,却忘记处理 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前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。