动态添加路由

587 阅读3分钟

1.理解路由表

在Vue Router中,使用router.addRoute可以动态添加路由。关于路由的组织方式,有两种常见的模式:嵌套路由和扁平化路由。

  1. 嵌套路由:

    • 优势: 嵌套路由更贴近实际页面结构,可以更好地反映应用的层级关系。适用于有层级结构的页面,比如页面内有多个组件组成,或者需要进行布局嵌套的情况。

    • 示例:

      const routes = [
        {
          path: '/dashboard',
          component: Dashboard,
          children: [
            {
              path: 'profile',
              component: Profile
            },
            {
              path: 'settings',
              component: Settings
            }
          ]
        }
      ];
      
  2. 扁平化路由:

    • 优势: 扁平化路由则更适用于简单的页面结构,每个路由平等地存在于路由层级中,不再嵌套。适用于页面较简单,层级关系不明显的情况。

    • 示例:

      const routes = [
        {
          path: '/dashboard',
          component: Dashboard
        },
        {
          path: '/profile',
          component: Profile
        },
        {
          path: '/settings',
          component: Settings
        }
      ];
      

为什么有些人更喜欢进行路由扁平化,取决于项目的实际需求和团队的开发习惯。在某些情况下,扁平化路由可能更容易维护,特别是在应用比较简单,不需要多层嵌套的情况下。另一方面,如果应用有复杂的层级结构,嵌套路由可以更好地组织和管理代码。因此,选择哪种方式取决于具体的项目需求和开发团队的偏好。

那扁平化的路由,是怎么被识别为一级路由 二级路由呢?

在扁平化的路由中,一级路由和二级路由之间的关系通常是通过path属性来识别的。一级路由的path是根据项目的根路径定义的,而二级路由的path则是在一级路由的基础上追加的。

例如,考虑以下扁平化路由的例子:

const routes = [
  {
    path: '/dashboard',
    component: Dashboard
  },
  {
    path: '/profile',
    component: Profile
  },
  {
    path: '/settings',
    component: Settings
  }
];

在这里,'/dashboard''/profile''/settings'分别可以被认为是一级路由。如果你想要将它们视为二级路由,可以调整path的定义。例如,将'/dashboard'改为'/dashboard/home''/profile'改为'/profile/info''/settings'改为'/settings/preferences'等。这样就可以在/dashboard/profile/settings下分别添加更多的二级路由。

总的来说,路由的层级关系在于path的定义。通过定义不同层级的path,你可以在Vue Router中实现一级路由和二级路由的结构。

2.动态添加路由

router.addRoute('name', {
    path:'/',
    component:()=>import('')
})
​

可以理解为将路由表中的路由删除掉,通过router.addRoute 去动态添加这部分被注释的路由信息

image.png

然后将这部分路由信息放到后端,

image.png

通过在views下创建对应后端返回中path字段的文件夹和文件,然后通过import.meta.glob('@renderer/views/**/*.vue');去检索到对应的.vue文件,将后端中component字段从字符串换成()=>import('')格式

image.png

image.png

需要把后端给前端返回的路由数据的component对应的value字符串修改成:component:()=>import('')
export const beforeEach = ( to )=>{
        initRouter()
    
    //当前路由没有匹配到任何路由记录
    if(to.matched.length == 0){
        router.push(to.fullPath)
    }
}
​
import { useMenuStore } from '@store/useMenuStore'
import router from '@router'
import { Parent } from '@interface/user'
export const beforeEach = ( to:any )=>{
​
    if( to.path == '/login' ){
        return ;
    }
​
    if( !localStorage.getItem('TOKEN') ){
        return '/login'
    }
​
    //动态添加路由
    initRouter();
​
    //当前路由没有匹配到任何路由记录
    if( to.matched.length == 0){
        router.push( to.fullPath );
    }
    return true;
}
​
interface Child{
    parentView: string;
    path: string;
    name: string;
    meta: any;
    redirect: string;
    children?: Child[] | null;
    component: any;
    id?: string | undefined;
    hidden?: boolean | undefined;
    alwaysShow?: boolean | undefined;
    query?: string | undefined;
}
​
interface Child extends Omit<Parent, 'children'> {
    children?: Child[] | null;
}
​
//1. 动态添加路由 => 整个过程
const initRouter = ()=>{
    let menu:Parent[] = useMenuStore().menu;
    let menuRouter: Child[] = filterRouter(menu);
    menuRouter = flatRoutes(menuRouter);
    menuRouter.forEach((item:any) => {
        router.addRoute(item.parentView == 'layout' ? 'layout' : '', item);
    });
}
​
//2. 把component 重构成 箭头函数的形式
const filterRouter = (menu: Parent[]): Child[] => {
    let arrRouter: Child[] = [];
    menu.forEach((item: any) => {
        var route: Child = {
            parentView: item.parentView,
            path: item.path,
            name: item.name,
            meta: item.meta,
            redirect: item.redirect,
            children: item.children ? filterRouter(item.children) : null,
            component: loadComponent(item.component)
        };
        arrRouter.push(route);
    });
    return arrRouter;
};
​
//3. 对于component的调整
const modules: Record<string, () => Promise<any>> = import.meta.glob('@renderer/views/**/*.vue');
const modulesMap: Record<string, () => Promise<any>> = {};
​
Object.keys(modules).forEach((key) => {
    const componentName = key.replace('/src/views', '').replace('.vue', '').replace('/index', '').replace('/', '');
    if (key.includes('index')) {
        modulesMap[`${componentName}/index`] = modules[key];
    }
    modulesMap[componentName] = modules[key];
});
​
//4. 根据modulesMap[key]返回对应的value值
const loadComponent = (component: string | null): (() => Promise<any>) | undefined => {
    if (component) {
        return modulesMap[component];
    }
    return;
};
​
​
//5. 路由扁平化 
const flatRoutes = (routes: Child[], breadcrumb: Child[] = []): Child[] => {
    let res: Child[] = [];
    routes.forEach((route: Child) => {
        const tmp = { ...route };
        if (tmp.children) {
            let childrenBreadcrumb: Child[] = [...breadcrumb];
            childrenBreadcrumb.push(route);
            let tmpRoute = { ...route };
            delete tmpRoute.children;
            res.push(tmpRoute);
            let childrenRoutes = flatRoutes(tmp.children, childrenBreadcrumb);
            childrenRoutes.map((item) => {
                res.push(item);
            });
        } else {
            let tmpBreadcrumb = [...breadcrumb];
            tmpBreadcrumb.push(tmp);
            res.push(tmp);
        }
    });
    return res;
};
​
//后置
export const afterEach = ()=>{
    console.log('后置');
}
​