vue-admin动态路由实现:后端菜单与addRoutes配置
vue-admin 后台管理模板中,菜单通常由后端按角色控制并返回,前端拿到数据后再生成路由和侧边栏。本文按原文思路整理动态路由落地时需要改动的几个位置:router/index.js、Vuex permission 模块、全局路由拦截和 Sidebar 渲染。一、精简 router/index.js 的静态路由
把 constantRoutes 中写死的静态路由删掉,只保留 login、404 等公共页面。其他业务路由不再写死在前端,而是后面通过接口获取。这样前端只负责公共页面,权限菜单由后端决定。
二、用 Vuex 的 permission 模块管理菜单
如果没有 store/modules/permission.js 就创建。该模块用 Vuex 状态管理保存菜单信息,最后把菜单渲染出去。核心状态有两个:routes 是拼接完成、需要暴露出去的路由;addRoutes 是后端返回的路由。SET_ROUTES 中把 constantRoutes 与后端路由拼接,并保存后端路由。
// store/modules/permission.js
import { constantRoutes } from '@/router'
import { getRouter } from '@/api/user'
const state = {
routes: [],
addRoutes: []
}
const mutations = {
SET_ROUTES: (state, routes) => {
state.addRoutes = routes
state.routes = constantRoutes.concat(routes)
}
}
const actions = {
generateRoutes({ commit }, roles) {
return new Promise(resolve => {
commit('SET_ROUTES', getRouter())
resolve(getRouter())
})
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
然后在 getter 中暴露 permission_routes,并在 store 入口注册 permission 模块:
permission_routes: state => state.permission.routes
import permission from './modules/permission'
原文示例中 generateRoutes 接收了 roles 参数,但核心逻辑里直接调用 getRouter() 获取后端路由。实际项目如果接口是异步的,需要结合 Promise 或 async/await 处理返回值;角色过滤最终应以后端返回结果为准。
三、在 src/permission.js 中拦截路由并添加动态路由
路由拦截阶段调用 permission/generateRoutes,拿到后端路由后,调用 router.addRoutes,并通过 next({ ...to, replace: true }) 重新进入目标路由,避免动态路由尚未挂载导致跳转异常。
store.dispatch('permission/generateRoutes').then(newRouter => {
router.addRoutes(filterRouter(newRouter))
next({ ...to, replace: true })
})
后端返回的路由不能直接给 vue-router 使用,需要遍历处理 component。若 component 等于字符串 "Layout",替换为导入的 Layout 组件;否则交给 loadComponent 做路由懒加载。存在 children 时递归处理。
import Layout from '@/layout'
const filterRouter = (router) => {
const res = []
router.forEach(a => {
if (a.component === 'Layout') {
a.component = Layout
} else {
a.component = loadComponent(a.component)
}
if (a.children && a.children.length > 0) {
a.children = filterRouter(a.children)
}
res.push(a)
})
return res
}
const loadComponent = (path) => {
// 路由懒加载(参数不能以 / 开头)
return (resolve) => require([`@/views/${path}.vue`], resolve)
}
这里要注意 component 路径格式。静态路由通常写成:
component: () => import('@/views/table/index')
后端返回时要写成:
component: "table/index"
也就是不能以斜杠 / 开头。@/views/ 前缀由前端在 require 中拼接,路径才能正确解析。如果后端直接返回 "/table/index",懒加载路径就会出错。
四、修改 Sidebar 导航菜单渲染
在 src/layout/components/Sidebar/index.vue 中,把菜单数据源改为 permission_routes:
<!-- 动态路由 -->
<sidebar-item v-for="route in permission_routes" :key="route.path" :item="route" :base-path="route.path" />
permission_routes 来自前面的 Vuex getter,这样侧边栏就会根据后端返回并转换后的路由渲染菜单。
五、关键点与适用场景
1. constantRoutes 只留下公共页面,业务菜单走后端接口。
2. 后端菜单需要经过 filterRouter 转换 component,Layout 组件单独处理,子路由递归处理。
3. loadComponent 使用 require 做路由懒加载,传入路径不能带前导斜杠。
4. 动态添加路由后使用 next({ ...to, replace: true }),保证路由挂载完成后再次进入目标地址。
5. 该方案适合 vue-admin 这类需要按角色返回菜单的后台管理系统;菜单和路由的控制权在后端,前端只负责格式转换与渲染。
以上实现点均围绕原文的 router、Vuex permission、全局路由拦截和 Sidebar 渲染展开。
Re: vue-admin动态路由实现:后端菜单与addRoutes配置
整理得挺清楚的,四个改动点串起来后思路顺多了。constantRoutes 只留公共页面、业务菜单完全由后端决定,这个方向确实更灵活,后面加权限也不用反复改前端静态路由。 我自己看下来最容易出问题的还是 filterRouter 里 component 的处理:后端返回 Layout 时要替换成真正的 Layout 组件,其他路径像 table/index 不能带前面的斜杠,不然拼接 @/views/ 后懒加载路径会错;有 children 时也要记得递归处理。另外 generateRoutes 那里如果 getRouter 是异步接口,直接 commit 可能会拿到空数据,最好等接口返回后再 resolve。路由拦截里 addRoutes 之后用 next({ ...to, replace: true }) 也很关键,不然动态路由还没挂上就跳转容易异常。 Sidebar 改成 permission_routes 这点也很重要,菜单和路由走同一份数据,后面维护起来会省心很多。你最后 loadComponent 那段好像没写完,期待补全,整体已经很实用了。Re: vue-admin动态路由实现:后端菜单与addRoutes配置
这个整理很实用,基本上把 vue-admin 动态路由要改的几个位置都串起来了。constantRoutes 只保留 login、404 这类公共页面,业务菜单完全由后端返回,这样前端权限维护会轻很多;permission 模块里 routes 和 addRoutes 分开存,SET_ROUTES 再把 constantRoutes 和后端路由拼起来,最后 getter 暴露 permission_routes 给 Sidebar 用,整条链路很清晰。 我比较关注两个容易出问题的点:一是 getRouter 如果是异步接口,generateRoutes 里直接 commit 同步结果肯定不行,得用 Promise 或 async/await 拿到数据再 resolve;二是后端返回的 component 路径,像 table/index 这种不能带开头的斜杠,Layout 又要单独替换成导入的 Layout,children 还得递归处理。路由拦截里 addRoutes 之后用 next 带上 to 和 replace 重新进入,也确实能避免动态路由还没挂载导致的跳转异常。感谢分享,这几个位置照着排查应该能省不少时间。Re: vue-admin动态路由实现:后端菜单与addRoutes配置
整理得挺清楚,按 router/index.js、Vuex permission、全局路由拦截和 Sidebar 四个位置来讲,动态路由落地时不容易乱。constantRoutes 只留公共页面、业务菜单交给后端返回这个思路挺实用,filterRouter 里对 Layout 单独替换、children 递归处理,还有 loadComponent 路径不能以斜杠开头,都是实际配置时很容易踩的坑。generateRoutes 那里提到 roles 参数和 getRouter 异步处理也很关键,直接当成同步返回值确实可能出问题。Sidebar 改用 permission_routes 后,菜单和路由就能共用同一份数据源,维护起来方便很多。末尾 loadCompon 好像没写完,如果方便的话可以补全懒加载那段,整体就更完整了。感谢分享。
页:
[1]