从API获取的Vue Router动态路由未显示,路径无法访问
我在通过API获取数据创建Vue Router动态路由时遇到了问题:添加的路由没有在router中显示,/examplegrid路径无法访问。我已经调用了router.addRoute方法,但检查router时,从API获取的路由数据并未存在。
相关代码
//router/index.js import { createRouter, createWebHistory } from 'vue-router'; import axiosInstance from '@/interceptors/axios.js'; import { api } from '@/api'; import Layout from '@/layout'; import Login from '@/login'; import store from '@/store'; const router = createRouter({ history: createWebHistory(), routes: [ { path: '/', component: Layout, children: [ { path: '', component: () => import('@/views/Dashboard'), meta: { requiresAuth: true, title: 'Dashboard' } } ] }, { path: '/login', component: Login }, { path: '/:catchAll(.*)', component: () => { window.location.href = '/pages/notfound.html'; }, }, ], }); async function createDynamicRoutes(userStamp, subsidiary, shouldCreateRoutes = true) { if (!shouldCreateRoutes) { return; } try { const response = await axiosInstance.post( api.dynamicRoutes.url, { UserStamp: userStamp, Subsidiary: subsidiary }, { headers: { 'Content-Type': 'application/json', }, } ); const data = response.data.menuPrivileges; if (!data) { return; } data.forEach(menuItem => { router.addRoute({ path: menuItem.path, component: Layout, children: [ { path: '', component: () => import('@/views' + menuItem.component), meta: { requiresAuth: true, title: menuItem.menuName } } ] }); }); console.log(JSON.stringify(router.options.routes)); } catch (error) { console.error('Error fetching dynamic routes:', error); } } createDynamicRoutes('ABC', 'XYZ'); router.beforeEach((to, from, next) => { const isAuthenticated = store.state.isAuthenticated; if (to.matched.some(record => record.meta.requiresAuth)) { if (!isAuthenticated) { next({ path: '/login' }); } else { next(); } } else { next(); } }); export default router;
API响应数据
{ "menuPrivileges":[ { "menuName":"Example Entry", "status":"A", "component":"/Grid", "path":"/ExampleGrid" } ] }
问题排查与解决
1. 别用router.options.routes查看动态路由
router.options.routes是初始化时的静态路由数组,动态添加的路由不会更新到这里,你用它打印看不到动态路由是正常的。要查看所有路由(包括动态添加的),改用console.log(router.getRoutes())。
2. 异步执行时机导致路由未及时加载
createDynamicRoutes是异步函数,直接调用后会继续执行后续代码,但路由添加要等API请求完成后才会执行。这会导致:
- 首次访问动态路由时,路由还没添加完成,被
catchAll通配符路由捕获跳转到404; - 路由守卫可能在动态路由添加前就执行,无法正确匹配权限。
解决方式:在应用初始化时先获取动态路由,再创建router。修改main.js如下:
// main.js import { createApp } from 'vue' import App from './App.vue' import { createRouter, createWebHistory } from 'vue-router'; import axiosInstance from '@/interceptors/axios.js'; import { api } from '@/api'; import Layout from '@/layout'; import Login from '@/login'; import store from '@/store'; async function initApp() { let dynamicRoutes = []; try { // 先请求动态路由数据 const response = await axiosInstance.post( api.dynamicRoutes.url, { UserStamp: 'ABC', Subsidiary: 'XYZ' }, { headers: { 'Content-Type': 'application/json' } } ); const data = response.data.menuPrivileges; if (data) { // 转换为路由格式 dynamicRoutes = data.map(menuItem => ({ path: menuItem.path, component: Layout, children: [ { path: '', component: () => import('@/views' + menuItem.component), meta: { requiresAuth: true, title: menuItem.menuName } } ] })); } } catch (error) { console.error('获取动态路由失败:', error); } // 创建router时合并静态路由和动态路由,注意把catchAll放最后 const router = createRouter({ history: createWebHistory(), routes: [ { path: '/', component: Layout, children: [ { path: '', component: () => import('@/views/Dashboard'), meta: { requiresAuth: true, title: 'Dashboard' } } ] }, { path: '/login', component: Login }, ...dynamicRoutes, // 动态路由放在catchAll前面 { path: '/:catchAll(.*)', component: () => { window.location.href = '/pages/notfound.html'; } } ] }); // 路由守卫逻辑不变 router.beforeEach((to, from, next) => { const isAuthenticated = store.state.isAuthenticated; if (to.matched.some(record => record.meta.requiresAuth)) { isAuthenticated ? next() : next({ path: '/login' }); } else { next(); } }); createApp(App).use(router).use(store).mount('#app'); } initApp();
3. 路由顺序问题
通配符路由/:catchAll(.*)会匹配所有未被前面路由匹配的路径,如果动态路由是在它之后添加的,访问动态路由时会先被它捕获。所以必须确保动态路由在通配符路由之前。
4. 路径大小写问题
API返回的路由路径是/ExampleGrid,而你访问的是/examplegrid,Vue Router默认区分大小写。解决方法二选一:
- 统一访问路径和路由路径的大小写;
- 在创建router时设置
strict: false忽略大小写:
const router = createRouter({ history: createWebHistory(), routes: [...], strict: false });
5. 组件路径检查
确认@/views/Grid组件存在,动态导入路径@/views/+/Grid会变成@/views//Grid,虽然大多数打包工具会自动处理,但最好让API返回的component去掉开头的斜杠,或者在代码中处理:component: () => import('@/views' + menuItem.component.replace(/^\/+/, ''))。
内容的提问来源于stack exchange,提问作者Noob

