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')
复制代码 后端返回时要写成:也就是不能以斜杠 / 开头。@/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 渲染展开。 |