react/vue vite ts项目中,自动引入路由文件、 import.meta.glob动态引入路由 无需手动引入

utils/autoRouteHelper.ts

复制代码
// src/utils/autoRouteHelper.ts
import { lazy } from "react";
import withLoading from "@/components/router/withLoading";

/** 自动生成某个文件夹下的子路由 */
interface RouteItem {
  path: string;
  element?: any;
  children?: RouteItem[];
  handle?: {
    title: string;
  };
}

/**
 * 生成嵌套路由树(保留 children 树结构,排除 components)
 * @param globModules 模块对象
 * @param baseDir 模块路径
 * @param titleMap 路由标题映射表
 * @returns 返回树形结构路由数据
 */
export function generateNestedRoutes(
  globModules: Record<string, () => Promise<any>>,
  baseDir: string,
  titleMap: Record<string, string> = {}
): RouteItem[] {
  const root: Record<string, any> = {};

  for (const [fullPath, loader] of Object.entries(globModules)) {
    if (fullPath.includes("/components/")) continue;

    const reg = new RegExp(`${baseDir}/(.*)/index\\.tsx$`, "i");
    const match = fullPath.match(reg);
    if (!match) continue;

    const subPath = match[1].replace(/\\/g, "/"); // windows 兼容
    const segments = subPath.split("/"); // ["pageb", "list"]
    let current = root;
    // let fullSegmentPath = "";

    for (let i = 0; i < segments.length; i++) {
      const segment = segments[i];
      // fullSegmentPath += (i === 0 ? "" : "/") + segment;

      if (!current[segment]) {
        current[segment] = {
          path: segment,
          childrenMap: {}
        };
      }

      // 最后一层,挂 element 和 title
      if (i === segments.length - 1) {
        current[segment].element = withLoading(lazy(loader));
        current[segment].handle = {
          title: titleMap[subPath.toLowerCase()] || segment
        };
      }

      current = current[segment].childrenMap;
    }
  }

  // 将 childrenMap 转为 children[]
  function convertToTree(obj: Record<string, any>): RouteItem[] {
    return Object.values(obj).map(({ childrenMap, ...rest }) => {
      const node: RouteItem = { ...rest };
      const children = convertToTree(childrenMap);
      if (children.length > 0) node.children = children;
      return node;
    });
  }

  return convertToTree(root);
}

/** 合并所有模块的默认导出,适合用于路由模块化(eager 模式) */
export function mergeModuleRoutes(
  modules: Record<string, any>
): any[] {
  const routes: any[] = [];

  Object.values(modules).forEach((mod: any) => {
    if (Array.isArray(mod.default)) {
      routes.push(...mod.default);
    } else if (mod.default) {
      routes.push(mod.default);
    }
  });

  return routes;
}

react路由使用

目录\router\modules\supplier.ts

import.meta.glob('@/pages/supplier/**/index.tsx'); 自动获取pages/supplier目录下的所有页面传递给generateNestedRoutes返回路由不限制层级,可以一二级也可以一二三四级别等等

复制代码
\router\modules
新增路由文件 如supplier.ts
import { lazy } from "react";
import withLoading from "@/components/router/withLoading";
import { generateNestedRoutes } from "@/utils/routerHelper";


const personalPageModules = import.meta.glob('@/pages/supplier/**/index.tsx');

const titleMap: Record<string, string> = {
  list: "供应商列表",
 
  // 你可以继续添加其他路径对应的标题
};



/** 供应商管理 */
const supplier: Array<any> = [
  {
    path: "supplier",
    element: withLoading(lazy(() => import("@/pages/supplier/index"))),
    
    handle: {
      title: "供应商管理",
    },
    children:[
     ...generateNestedRoutes(personalPageModules,"supplier",titleMap)
    ]
  },
];
console.log(supplier,"供应商管理")
export default supplier;

router/index.ts目录中
const modules = import.meta.glob('./modules/*.ts', { eager: true });

...mergeModuleRoutes(modules),
自动获取modules目录下的所有路由文件

vue路由中使用

routerHelper.ts

复制代码
// src/utils/autoRouteHelper.ts
// src/utils/autoRouteHelper.ts
/**
 * 自动生成某个模块文件夹下的子路由(用于 Vue Router)
 */

interface TitleMap {
  [key: string]: string;
}

interface AutoRouteOptions {
  /** 模块目录,如 'views/merchantmanage' */
  baseDir: string;
  /** 路由标题映射表 */
  titleMap?: TitleMap;
  /** 排除目录数组,如 ['components', 'common'] */
  excludeDirs?: string[];
}

/**
 * 自动生成某个目录下的子路由,支持多级目录,支持排除子目录
 * @param globModules import.meta.glob 的结果
 * @param options 配置项
 */
export function generateChildrenRoutes(
  globModules: Record<string, () => Promise<any>>,
  options: AutoRouteOptions
) {
  const { baseDir, titleMap = {}, excludeDirs = [] } = options;

  return Object.entries(globModules)
    .map(([fullPath, loader]) => {
      // 1. 判断是否命中排除目录
      const shouldExclude = excludeDirs.some((dir) =>
        fullPath.includes(`${baseDir}/${dir}/`)
      );
      if (shouldExclude) return null;

      // 2. 提取 baseDir 后的子路径部分
      const match = fullPath.match(new RegExp(`${baseDir}/(.+)\\.vue$`));
      if (!match) return null;

      const subPath = match[1]; // 例:user/detail/index 或 home
      const segments = subPath.split('/');

      // 构建路由 path,忽略 index
      let routePath = segments
        .map((seg) => (seg === 'index' ? '' : seg))
        .filter(Boolean)
        .join('/');

      if (!routePath) routePath = '';

      const routeName = routePath.replace(/\//g, '-');

      return {
        path: routePath,
        name: routeName,
        component: loader,
        meta: {
          title: titleMap[routePath.toLowerCase()] || segments.at(-1) || routePath
        }
      };
    })
    .filter(Boolean) as any[];
}


/** 合并所有模块的默认导出,适合用于路由模块化(eager 模式) */
export function mergeModuleRoutes(
  modules: Record<string, any>
): any[] {
  const routes: any[] = [];

  Object.values(modules).forEach((mod: any) => {
    if (Array.isArray(mod.default)) {
      routes.push(...mod.default);
    } else if (mod.default) {
      routes.push(mod.default);
    }
  });

  return routes;
}

/使用实例

复制代码
const modules = import.meta.glob('@/views/merchantmanage/**/*.vue');

const routes = generateChildrenRoutes(modules, {
  baseDir: 'views/merchantmanage',
  excludeDirs: ['components', 'fragments', 'common'],
  titleMap: {
    user: '用户管理',
    'user/detail': '用户详情'
  }
});

这样就会排出默认components或者自定义的目录作为路由

无需要在手动写进去再去引入

相关推荐
YukiMori231 小时前
一个有趣的原型继承实验:为什么“男人也会生孩子”?从对象赋值到构造函数继承的完整推演
前端·javascript
摸鱼的春哥2 小时前
惊!黑客靠AI把墨西哥政府打穿了,海量数据被黑
前端·javascript·后端
小兵张健2 小时前
Playwright MCP 截图标注方案调研(推荐方案1)
前端·javascript·github
我叫黑大帅5 小时前
Vue3和Uniapp的爱恨情仇:小白也能懂的跨端秘籍
前端·javascript·vue.js
None3215 小时前
【NestJs】使用Winston+ELK分布式链路追踪日志采集
javascript·node.js
Qinana6 小时前
从代码到智能体:MCP 协议如何重塑 AI Agent 的边界
前端·javascript·mcp
不会敲代码16 小时前
从入门到进阶:手写React自定义Hooks,让你的组件更简洁
前端·react.js
洋洋技术笔记6 小时前
Vue实例与数据绑定
前端·vue.js
Marshall1516 小时前
zzy-scroll-timer:一个跨框架的滚动定时器插件
前端·javascript
明月_清风8 小时前
打字机效果优化:用 requestAnimationFrame 缓冲高频文字更新
前端·javascript