logo
  • 指南
  • 配置
  • 插件
  • API
  • 示例
  • 社区
  • Modern.js 2.x 文档
  • 简体中文
    • 简体中文
    • English
    • 开始
      介绍
      快速上手
      版本升级
      名词解释
      技术栈
      核心概念
      页面入口
      构建工具
      Web 服务器
      基础功能
      路由
      路由基础
      配置式路由
      数据管理
      数据获取
      数据写入
      数据缓存
      渲染
      服务端渲染(SSR)
      服务端流式渲染(Streaming SSR)
      渲染缓存
      静态站点生成(SSG)
      渲染预处理 (Render Preprocessing)
      样式开发
      引入 CSS
      使用 CSS Modules
      使用 CSS-in-JS
      使用 Tailwind CSS
      HTML 模板
      引用静态资源
      引用 JSON 文件
      引用 SVG 资源
      引用 Wasm 资源
      调试
      数据模拟(Mock)
      网络代理
      使用 Rsdoctor
      使用 Storybook
      测试
      Playwright
      Vitest
      Jest
      Cypress
      路径别名
      环境变量
      构建产物目录
      部署应用
      进阶功能
      使用 Rspack
      使用 BFF
      基础用法
      运行时框架
      扩展 BFF Server
      扩展一体化调用 SDK
      文件上传
      跨项目调用
      优化页面性能
      代码分割
      静态资源内联
      产物体积优化
      React Compiler
      提升构建性能
      浏览器兼容性
      配置底层工具
      源码构建模式
      服务端监控
      Monitors
      日志事件
      指标事件
      国际化
      基础概念
      快速开始
      配置说明
      语言检测
      资源加载
      路由集成
      API 参考
      高级用法
      最佳实践
      自定义 Web Server
      专题详解
      模块联邦
      简介
      开始使用
      应用级别模块
      服务端渲染
      部署
      集成国际化能力
      常见问题
      依赖安装问题
      命令行问题
      构建相关问题
      热更新问题
      已下线功能
      📝 编辑此页面
      上一页路由基础下一页数据获取

      #配置式路由

      默认情况下,Modern.js 推荐使用 约定式路由 作为路由定义的方式。同时,Modern.js 也提供了一个配置式路由的能力,其可以和约定式路由一起使用,或两者分别单独使用。

      #何时使用配置式路由

      配置式路由在以下场景中特别有用:

      • 需要灵活的路由控制:当文件结构无法直接映射到所需的 URL 结构时
      • 多路由指向同一组件:需要将不同的 URL 路径指向同一个页面组件
      • 条件性路由:根据不同条件动态配置路由
      • 遗留项目迁移:从其他路由系统迁移到 Modern.js 时保持原有的路由结构
      • 复杂的路由命名:需要自定义路由路径而不受文件目录限制

      #基本用法

      在 src 或每个入口目录下,你可以定义一个 modern.routes.[tj]s 文件,通过该文件,你可以对路由进行配置:

      import { defineRoutes } from '@modern-js/runtime/config-routes'
      
      export default defineRoutes(({ route, layout, page, $ }, fileRoutes) => {
        return [
          route("home.tsx", "/"),
        ]
      })

      #函数签名说明

      defineRoutes 接受一个回调函数,该回调函数有两个参数:

      1. routeFunctions: 包含 route、layout、page、$ 函数的对象
      2. fileRoutes: 约定式路由生成的路由配置数组

      路由函数的基本签名:

      • 第一个参数:相对于 modern.routes.[tj]s 的文件路径
      • 第二个参数:路由路径(可选,必须是字符串)
      • 第三个参数:子路由数组(可选)

      #路由函数

      Modern.js 提供了四个主要的路由配置函数:

      路径说明

      所有路由函数的第一个参数(路径)都是相对路径,会与父级路径拼接生成最终的路由路径:

      • 根路径:"/" 或 "" 表示当前层级的根路径
      • 相对路径:子路由的路径会相对于父级路径进行拼接
      • 动态路径:使用 :param 语法表示动态参数
      • 通配路径:使用 "*" 语法匹配所有路径
      Info
      • route、layout、page、$ 等函数的第一个参数(组件文件路径)必须指向当前项目中的真实文件,暂不支持 node_modules 及 Monorepo 下其他仓库中的文件
      • 不支持使用路径别名(例如 @/pages/...、~/pages/... 等);请使用相对于 modern.routes.[tj]s 的相对路径。

      #route 函数

      route 函数是通用的路由配置函数,会根据是否有子路由自动决定生成页面路由还是布局路由,可以代替 layout, page, $等函数。

      export default defineRoutes(({ route }, fileRoutes) => {
        return [
          // 无子路由时,自动生成页面路由
          route("home.tsx", "/"),
          route("about.tsx", "about"),
      
          // 有子路由时,自动生成布局路由
          // dashboard/layout.tsx 需要包含 <Outlet> 来渲染子路由
          route("dashboard/layout.tsx", "dashboard", [
            route("dashboard/overview.tsx", "overview"),  // 生成路径:/dashboard/overview
            route("dashboard/settings.tsx", "settings"),  // 生成路径:/dashboard/settings
          ]),
      
          // 动态路由
          route("products/detail.tsx", "products/:id"),
      
          // 多路径指向同一组件
          route("user/profile.tsx", "user"),
          route("user/profile.tsx", "profile"),
        ]
      })

      使用场景:

      • 快速配置路由,无需明确指定是页面还是布局
      • 简化路由配置的复杂度

      #layout 函数

      layout 函数专门用于配置布局路由,始终生成布局组件,必须包含子路由:

      export default defineRoutes(({ layout, page }, fileRoutes) => {
        return [
          // 生成布局路由,路径为 "/auth",必须包含子路由
          layout("auth/layout.tsx", "auth", [
            page("auth/login/page.tsx", "login"),      // 生成路径:/auth/login
            page("auth/register/page.tsx", "register"), // 生成路径:/auth/register
          ]),
        ]
      })

      使用场景:

      • 需要明确指定某个组件为布局组件
      • 为多个页面提供共同的布局结构
      • 需要在多个路由间共享导航、侧边栏等UI组件

      #page 函数

      page 函数专门用于配置页面路由,始终生成页面组件:

      export default defineRoutes(({ layout, page }, fileRoutes) => {
        return [
          layout("dashboard/layout.tsx", "dashboard", [
            page("dashboard/overview.tsx", "overview"),  // 生成路径:/dashboard/overview
            page("dashboard/settings.tsx", "settings"),  // 生成路径:/dashboard/settings
          ]),
        ]
      })

      使用场景:

      • 需要明确指定某个组件为页面组件
      • 确保组件不包含 <Outlet> 子组件渲染
      • 提供更清晰的语义表达

      #$ 函数

      $ 函数专门用于配置通配路由,用于处理未匹配的路由:

      export default defineRoutes(({ layout, page, $ }, fileRoutes) => {
        return [
          layout("blog/layout.tsx", "blog", [
            page("blog/page.tsx", ""),                    // 生成路径:/blog
            page("blog/[id]/page.tsx", ":id"),            // 生成路径:/blog/:id
            $("blog/$.tsx", "*"),                         // 通配路由,匹配 /blog 下的所有未匹配路径
          ]),
        ]
      })

      使用场景:

      • 自定义 404 页面
      • 处理特定路径下的所有未匹配请求
      Tip

      $ 函数与约定式路由中的 $.tsx 文件功能相同,用于捕获未匹配的路由请求。

      #配置路由

      #基本示例

      export default defineRoutes(({ page }, fileRoutes) => {
        return [
          // 使用 page 函数明确指定页面路由
          page("home.tsx", "/"),
          page("about.tsx", "about"),
          page("contact.tsx", "contact"),
        ]
      })

      #无路径路由

      当不指定路径时,路由会继承父级路径:

      export default defineRoutes(({ layout, page }, fileRoutes) => {
        return [
          // 使用 layout 函数明确指定布局路由
          // auth/layout.tsx 需要包含 <Outlet> 来渲染子路由
          layout("auth/layout.tsx", [
            page("login/page.tsx", "login"),
            page("register/page.tsx", "register"),
          ]),
        ]
      })

      上述配置会生成:

      • /login → auth/layout.tsx + login/page.tsx
      • /register → auth/layout.tsx + register/page.tsx

      #多路径指向同一组件

      export default defineRoutes(({ page }, fileRoutes) => {
        return [
          page("user.tsx", "user"),
          page("user.tsx", "profile"),
          page("user.tsx", "account"),
        ]
      })

      #动态路由

      配置式路由支持动态路由参数:

      export default defineRoutes(({ page }, fileRoutes) => {
        return [
          // 必需参数
          page("blog/detail.tsx", "blog/:id"),
      
          // 可选参数
          page("blog/index.tsx", "blog/:slug?"),
        ]
      })

      #路由相关文件自动查找

      配置式路由会自动查找组件的配套文件,无需手动配置。对于 modern.routes.[tj]s 中指定的任意组件文件,Modern.js 会自动查找以下配套文件:

      // modern.routes.[tj]s
      export default defineRoutes(({ route }, fileRoutes) => {
        return [
          route("pages/profile.tsx", "profile"),
          route("pages/user/detail.tsx", "user/:id"),
        ]
      })

      Modern.js 会自动查找并加载:

      • pages/profile.data.ts → 数据加载器
      • pages/profile.config.ts → 路由配置
      • pages/profile.error.tsx → 错误边界
      • pages/profile.loading.tsx → Loading 组件

      #查找规则

      • 文件位置:配套文件必须与组件文件在同一目录
      • 文件命名:配套文件名与组件文件名相同(不包括扩展名)
      • 自动发现:Modern.js 自动发现并加载这些文件
      Tip

      更多关于数据获取的详细信息,请参考 数据获取 文档。关于 Loading 组件的使用,请参考 约定式路由 - Loading。

      #路由合并

      配置式路由可以与约定式路由一起使用,你可以通过修改 fileRoutes 参数来实现路由的合并:

      1. 覆盖路由:可以主动移除约定式路由并替换为配置式路由
      2. 补充路由:可以在约定式路由基础上添加新的配置式路由
      3. 混合使用:可以在约定式布局路由下添加配置式子路由
      Info

      当前路由结构可以通过 modern routes 命令查看

      #合并示例

      以下示例展示了配置式路由与约定式路由的合并方式:

      // modern.routes.ts
      import { defineRoutes } from '@modern-js/runtime/config-routes';
      
      export default defineRoutes(({ page }, fileRoutes) => {
        // 场景1:覆盖约定式路由
        // 移除原有的 shop 路由,替换为自定义组件
        const shopPageIndex = fileRoutes[0].children?.findIndex(
          route => route.id === 'three_shop/page',
        );
        fileRoutes[0].children?.splice(shopPageIndex!, 1);
        fileRoutes[0].children?.push(page('routes/CustomShop.tsx', 'shop'));
      
        // 场景2:补充约定式路由
        // 添加配置式路由,无约定式路由对应
        fileRoutes[0].children?.push(page('routes/Settings.tsx', 'settings'));
      
        // 场景3:混合嵌套路由
        // 在约定式布局路由下添加配置式子路由
        const userRoute = fileRoutes[0].children?.find(
          (route: any) => route.path === 'user',
        );
        if (userRoute?.children) {
          userRoute.children.push(page('routes/user/CustomTab.tsx', 'custom-tab'));
        }
      
        // 场景4:自动发现配套文件
        // Product.tsx 会自动发现 Product.data.ts 和 Product.error.tsx
        fileRoutes[0].children?.push(page('routes/Product.tsx', 'product/:id'));
      
        return fileRoutes;
      });

      #调试路由

      由于路由合并后的最终结构可能不够直观,Modern.js 提供了调试命令来帮助你查看完整路由信息:

      # 生成路由结构分析报告
      npx modern routes

      该命令会在 dist/routes-inspect.json 文件中生成最终的路由结构,帮助你了解合并后的完整路由信息。

      #报告文件示例

      #单入口场景

      dist/routes-inspect.json
      {
        "routes": [
          {
            "path": "/",
            "component": "@_modern_js_src/routes/page",
            "data": "@_modern_js_src/routes/page.data",
            "clientData": "@_modern_js_src/routes/page.data.client",
            "error": "@_modern_js_src/routes/page.error",
            "loading": "@_modern_js_src/routes/page.loading"
          },
          {
            "path": "/dashboard",
            "component": "pages/admin",
            "config": "pages/admin.config",
            "error": "pages/admin.error"
          },
          {
            "path": "/user",
            "component": "layouts/user",
            "children": [
              {
                "path": "/user/profile",
                "component": "@_modern_js_src/routes/user/profile",
                "data": "@_modern_js_src/routes/user/profile.data"
              }
            ]
          },
          {
            "path": "@_modern_js_src/routes/blog/:id",
            "component": "blog/detail",
            "params": ["id"],
            "data": "blog/detail.data",
            "loading": "blog/detail.loading"
          },
          {
            "path": "/files/*",
            "component": "@_modern_js_src/routes/files/list"
          }
        ]
      }

      #多入口场景

      在多入口项目中,报告文件会按照入口名称进行分组,其中 key 为 entryName:

      dist/routes-inspect.json
      {
        "main": {
          "routes": [
            {
              "path": "/",
              "component": "@_modern_js_src/routes/page",
              "data": "@_modern_js_src/routes/page.data",
              "error": "@_modern_js_src/routes/page.error",
              "loading": "@_modern_js_src/routes/page.loading"
            },
            {
              "path": "/dashboard",
              "component": "@_modern_js_src/routes/dashboard",
              "config": "@_modern_js_src/routes/dashboard.config"
            }
          ]
        },
        "admin": {
          "routes": [
            {
              "path": "/",
              "component": "@_modern_js_src/routes/dashboard",
              "data": "@_modern_js_src/routes/dashboard.data",
              "clientData": "@_modern_js_src/routes/dashboard.data.client",
              "config": "@_modern_js_src/routes/dashboard.config"
            },
            {
              "path": "/users",
              "component": "@_modern_js_src/routes/users",
              "data": "@_modern_js_src/routes/users.data",
              "error": "@_modern_js_src/routes/users.error",
              "loading": "@_modern_js_src/routes/users.loading"
            }
          ]
        }
      }

      #路由相关文件字段说明

      报告中除了基本的路由信息外,还会显示每个路由找到的相关文件:

      • data: 服务端数据加载文件(.data.ts),用于在服务端获取数据
      • clientData: 客户端数据加载文件(.data.client.ts),用于在客户端重新获取数据
      • error: 错误边界文件(.error.tsx),用于处理路由渲染错误
      • loading: 加载状态组件文件(.loading.tsx),用于显示数据加载中的状态
      • config: 路由配置文件(.config.ts),用于配置路由元数据

      这些字段都是可选的,只有在找到对应文件时才会在报告中显示。通过查看这些字段,你可以快速了解每个路由的完整文件结构。