在 Vue3 + Vite 项目里,动态路由一般有 3 种常见场景,通常用的是 「后端返回菜单 → 前端动态生成路由」 这一套。
✅ 一、先理解什么是动态路由
动态路由 ≠ /user/:id 这种参数路由。
在后台系统里通常指:
👉 运行时动态 addRoute
登录后才加载页面
根据权限加载页面
后端控制菜单
例如:
{ "name": "WeakPlan", "path": "/plan/weak", "component": "plan/weak/index" }
前端收到后动态注册。
✅ 二、目录结构(推荐)
src
├── router
│ ├── index.ts # 路由入口
│ ├── dynamic.ts # 动态路由处理
│ └── modules
├── views
│ ├── plan
│ │ └── weak
│ │ └── index.vue
✅ 三、基础路由(固定路由)
先写不会变的页面:
// router/index.ts
import { createRouter, createWebHistory } from 'vue-router'
export const constantRoutes = [
{
path: '/login',
component: () => import('@/views/login/index.vue')
},
{
path: '/',
component: () => import('@/layout/index.vue'),
redirect: '/dashboard'
}
]
const router = createRouter({
history: createWebHistory(),
routes: constantRoutes
})
export default router
✅ 四、核心:动态加载 view(Vite 写法)
⚠️ Vite 不能用 webpack 的 require。
必须用:
⭐ import.meta.glob(重点)
// router/dynamic.ts
// 自动扫描 views 下所有页面
const modules = import.meta.glob('.../views/**/*.vue')
得到类似:
{
".../views/plan/weak/index.vue": () => import(...)
}
✅ 五、后端路由 → Vue Router 转换
示例后端数据
{
"path": "/plan/weak",
"name": "WeakPlan",
"component": "plan/weak/index"
}
转换函数
import router from './index'
const modules = import.meta.glob('.../views/**/*.vue')
export function loadAsyncRoutes(routes: any[]) {
routes.forEach(route => {
const componentPath =
`../views/${route.component}.vue`
route.component = modules[componentPath]
router.addRoute(route)
})
}
✅ 六、登录后动态注册(真正项目写法)
// login 成功后
const menuRoutes = await getUserMenus()
loadAsyncRoutes(menuRoutes)
✅ 七、刷新页面丢失路由(必须解决)
动态路由最大坑:
👉 F5 刷新后 addRoute 消失
解决方案:
在路由守卫里重新加载
let isLoaded = false
router.beforeEach(async (to, from, next) => {
if (!isLoaded && token存在) {
const routes = await getUserMenus()
loadAsyncRoutes(routes)
isLoaded = true
next({ ...to, replace: true })
return
}
next()
})
✅ 八、支持 Layout 嵌套路由(后台必备)
后端返回:
{
"path": "/plan",
"component": "Layout",
"children": [
{
"path": "weak",
"component": "plan/weak/index"
}
]
}
前端处理:
import Layout from '@/layout/index.vue'
if (route.component === 'Layout') {
route.component = Layout
}