React Router 框架模式核心配置文件react-router.config.ts全指南
【免费下载链接】react-routerDeclarative routing for React项目地址: https://gitcode.com/GitHub_Trending/re/react-router
本文围绕 React Router 框架模式下的可选配置文件react-router.config.ts展开,系统讲解它在应用中的加载时机、默认值与每个配置项的具体用法,并结合仓库源码揭示其底层解析与合并逻辑。读完本文,你将掌握通过这份配置灵活调整 SSR / SPA 模式、构建目录、预渲染、懒路由发现、Server Bundles 等应用形态的完整实战方法。
文件定位与作用
react-router.config.ts是 React Router 框架模式(MODES: framework)下的项目级配置文件,位于项目根目录。它允许你定制 React Router 应用的关键行为,包括服务端渲染方式、目录位置以及构建设置。
该文件是可选的:不提供它时,所有选项均使用默认值,应用依然可以正常运行。需要自定义时,只需在项目根目录创建该文件即可。
相关阅读:关于配置文件在框架模式整体约定中的位置,可参考 framework-conventions/index.md;与它常常同时出现的还有定义路由的 routes.ts.md。
配置是如何被加载的
从源码层面看,这份配置的读取、校验、热更新逻辑位于 packages/react-router-dev/config/config.ts:
- 开发服务器与构建工具会通过
findEntry(root, "react-router.config", ...)在项目根目录定位该文件(config.ts),因此文件名必须精确为react-router.config.ts(扩展名亦可为.js等被findEntry识别的形式)。 resolveConfig在读取后要求模块必须提供 default export,否则会报must provide a default export/must export a config错误(config.ts)。- 开发模式下通过
chokidar监听该文件,创建、删除或修改配置会触发重新解析并自动生效(config.ts),无需手动重启开发服务器。 - 用户配置会与
presets预设产出的配置深度合并,再填入默认值生成“已解析配置”(ResolvedReactRouterConfig),最终被整体deepFreeze冻结,防止运行时被意外修改(config.ts)。 - 若项目通过 CLI 使用
react-router相关命令(例如react-router routes --config <file>指定自定义配置文件),也会读取该配置(cli/run.ts)。
基础形态
Config类型由@react-router/dev/config导出,其真实定义为ReactRouterConfig(见 packages/react-router-dev/config.ts)。最简用法如下:
import type { Config } from "@react-router/dev/config"; export default { appDirectory: "app", buildDirectory: "build", ssr: true, prerender: ["/", "/about"], } satisfies Config;export default的对象类型即为Config,使用satisfies Config可以让 TypeScript 在写错键名时立刻给出类型报错,同时保留字面量的精确类型用于后续推导。
仓库内多个可运行的示例项目直接展示了真实用法,例如 playground/framework/react-router.config.ts:
import type { Config } from "@react-router/dev/config"; export default { subResourceIntegrity: true, future: { unstable_optimizeDeps: true, }, } satisfies Config;配置项详解
以下逐个介绍react-router.config.ts支持的配置项。除文档公开的选项外,文中还补充了从源码类型ReactRouterConfig(config.ts)确认到的最新顶层选项。
appDirectory
app目录相对于项目根目录的路径,默认值为"app"。路由、入口文件、根组件等框架约定的源码都放在此目录下。
export default { appDirectory: "src", } satisfies Config;将应用源码放在src目录是常见的项目组织方式。注意此值必须是相对路径,解析后会转换为绝对路径存入ResolvedReactRouterConfig.appDirectory(config.ts)。
buildDirectory
构建输出目录相对于项目根目录的路径,默认值为"build"。客户端产物、服务端构建产物都会输出到此目录内。
export default { buildDirectory: "dist", } satisfies Config;serverBuildFile
服务端构建产物的文件名,默认值为"index.js"。该文件需要以.js结尾,并最终部署到你的服务器上。
export default { serverBuildFile: "server.js", } satisfies Config;ssr
是否启用服务端渲染,默认值为true。
ssr: true:React Router 会在服务端渲染你的应用。ssr: false:React Router 会在构建期请求/路径并把渲染结果连同资源保存为一个index.html文件,使应用可以无需服务端渲染地作为纯 SPA 部署。这就是所谓的 “SPA Mode”。
export default { ssr: false, // 禁用服务端渲染,进入 SPA 模式 } satisfies Config;仓库中 playground/framework-spa/react-router.config.ts 与 playground/rsc-vite-framework/react-router.config.ts 均使用ssr: false演示纯 SPA 部署形态。完整说明参见 SPA Mode。
从源码可看到,
ssr: false时构建系统会对/路径发起一次请求并保存产物为index.html(类型注释见 config.ts),这也是“预渲染 + 静态托管”模式能够工作的基础。
prerender
在构建期将指定 URL 预渲染为 HTML 文件的路径列表。它有三种写法:
export default { // 方式一:静态数组 prerender: ["/", "/about", "/contact"], // 方式二:动态函数(可结合 getStaticPaths 读取已有路由) prerender: async ({ getStaticPaths }) => { const paths = await getStaticPaths(); return ["/", ...paths]; }, // 方式三:对象形式,用于开启并发预渲染 prerender: { paths: ["/", "/about", "/contact"], concurrency: 4, }, } satisfies Config;其中getStaticPaths会返回应用中全部静态路由路径,便于把“所有静态页面”都纳入预渲染而不必手写列表。
关于concurrency,源码类型注释给出了明确的语义:默认值为1,即完全不并发、串行执行;设为大于 1 的值可开启并发预渲染,能加快构建速度,但会消耗更多资源,也会向源服务器 / CMS 发出更多并发请求(config.ts)。该选项的类型还可接受boolean(见PrerenderPaths,config.ts)。
仓库中 playground/vite-plugin-cloudflare/react-router.config.ts 使用prerender: ["/static"]演示静态路径预渲染,playground/rsc-vite-framework/react-router.config.ts 则预渲染了/与/server-loader。详细说明参见 Pre-Rendering。
routeDiscovery
配置客户端如何发现和加载路由,即 “Lazy Route Discovery” 行为。默认值为mode: "lazy"且manifestPath: "/__manifest"。
| mode | 行为 |
|---|---|
"lazy"(默认) | 路由随用户导航而按需发现加载 |
"initial" | 关闭懒发现,全部路由随初始 HTML 文档一次性加载 |
配套参数:
manifestPath:仅在mode: "lazy"下生效,用于自定义 manifest 请求路径,默认/__manifest。
export default { // 开启懒路由发现(默认行为) routeDiscovery: { mode: "lazy", manifestPath: "/__manifest", }, // 使用自定义 manifest 路径 routeDiscovery: { mode: "lazy", manifestPath: "/custom-manifest", }, // 关闭懒发现,初始即包含全部路由 routeDiscovery: { mode: "initial" }, } satisfies Config;需要说明的是,对象形式下只能同时给出其中一组键——mode: "lazy"时可配manifestPath,mode: "initial"时不允许再带manifestPath,这一约束在类型定义routeDiscovery的联合类型中已被强制执行(config.ts)。设计动机与原理参见 Lazy Route Discovery。
future
用于开启未来版本特性(future flags)的开关集合。
export default { future: { // 在此开启 future flags }, } satisfies Config;从当前仓库源码 packages/react-router-dev/config/config.ts 看,当前仍处于unstable_前缀阶段的特性有两个:
unstable_enableNodeReadableStream:启用 Node.jsReadableStream支持,默认false;unstable_optimizeDeps:优化依赖预构建,默认false。
同时,配置解析逻辑会识别一批v8_前缀的新命名(如v8_splitRouteModules、v8_viteEnvironmentApi、v8_passThroughRequests、v8_middleware、v8_trailingSlashAwareDataRequests等)并对旧的unstable_命名给出迁移提示,部分曾经的 unstable 选项(如unstable_subResourceIntegrity)已升级为顶层稳定选项(config.ts)。
历史与迁移说明可参考 Future Flags。
presets
平台与工具集成预设数组。第三方平台(如 Cloudflare 等)或内部工具可通过 preset 提供一组预先封装好的配置,帮助应用开箱即用地接入目标运行时。
export default { presets: [ // 在此添加 preset ], } satisfies Config;源码层面的预设类型定义(config.ts)显示,一个Preset需要提供:
name:预设名称;reactRouterConfig:接收用户已写的配置reactRouterUserConfig,返回合并用的配置片段(ConfigPreset,即除presets外的全部配置项);reactRouterConfigResolved(可选):在配置全部解析完成(ResolvedReactRouterConfig)后执行的回调,可用于校验或后处理。
加载时,resolveConfig会逐个执行 preset 的reactRouterConfig,再把结果与用户配置合并(用户配置优先级更高),合并规则中presets键自身会被排除,避免递归套娃(config.ts)。平台接入详情参见 Presets。
buildEnd
完整构建结束后的回调函数,接收以下参数:
export default { buildEnd: async ({ buildManifest, reactRouterConfig, viteConfig, }) => { // 在这里编写自定义构建逻辑 console.log("Build completed!"); }, } satisfies Config;其中buildManifest为BuildManifest | undefined,包含路由清单(若启用 server bundles 还包含 bundle 与路由的映射关系);reactRouterConfig是已解析配置;viteConfig是 Vite 解析后的配置(config.ts)。适合用于产物上传、通知、生成额外清单文件等收尾工作。
serverBundles
一个为不同路由分配不同服务端 bundle的函数。函数接收{ branch }(当前路由分支,仅暴露id、path、file、index四个属性的只读子集,见 config.ts),返回的字符串作为 bundle ID,并用作服务端构建目录下的子目录名。
export default { serverBundles: ({ branch }) => { // 根据路由分支返回 bundle ID return branch.some((route) => route.id === "admin") ? "admin" : "main"; }, } satisfies Config;典型场景是把管理后台等包含重型依赖的路由拆到独立 bundle,使主应用体积更小。详细机制参见 Server Bundles。
serverModuleFormat
服务端构建产物的模块格式,默认值为"esm"。
export default { serverModuleFormat: "cjs", // 或 "esm" } satisfies Config;可选值仅"esm"与"cjs"两种,这在类型ServerModuleFormat中被严格限定(config.ts)。若你的服务器运行环境对 ESM 支持不佳,可切换为"cjs"。
allowedActionOrigins
允许向其提交表单 action 的外部来源(origin)主机名列表。该限制只作用于 UI 路由,不适用于 resource routes(即无组件、纯接口型路由)。支持 micromatch 风格的 glob 匹配:
*:匹配一个段(segment);**:匹配多个段。
export default { allowedActionOrigins: [ "example.com", "*.example.com", // 匹配 sub.example.com "**.example.com", // 匹配 sub.domain.example.com ], } satisfies Config;该选项的作用是对从外部来源向 UI 路由发起的提交做额外的跨站请求伪造(CSRF)防护。若未配置,源码解析时该值默认为false(config.ts),ResolvedReactRouterConfig.allowedActionOrigins的类型为string[] | false(config.ts)。
如何在运行时动态设置
如果这份配置需要在运行时(而非构建时)决定,可以在自定义服务器的服务端构建对象上直接覆盖该值。以 Express 为例:
import express from "express"; import { createRequestHandler } from "@react-router/express"; import type { ServerBuild } from "react-router"; export const app = express(); async function getBuild() { let build: ServerBuild = await import( "virtual:react-router/server-build" ); return { ...build, allowedActionOrigins: process.env.NODE_ENV === "development" ? undefined : ["staging.example.com", "www.example.com"], }; } app.use(createRequestHandler({ build: getBuild }));这段代码会先在开发环境放开限制(undefined),在生产环境仅放行staging.example.com与www.example.com。Express 适配器相关用法见 packages/react-router-express/server.ts。
源码确认的额外顶层选项
除上述文档公开选项外,从 ReactRouterConfig 类型定义中还可以确认两个已稳定为顶层配置的选项:
splitRouteModules
是否自动将路由模块按需拆分为多个 chunk,默认值为true。可设为:
false:保持路由模块为单个 chunk;true:在可能时自动拆分(默认);"enforce":强制要求所有路由都可被拆分。
类型为SplitRouteModulesOption = boolean | "enforce"(config.ts)。该能力早期曾是 unstable future flag(unstable_splitRouteModules),现已在类型中升级为顶层选项。
subResourceIntegrity
是否为资源 script 标签生成 SRI(子资源完整性)哈希,默认值为false(config.ts)。这也是此前future.unstable_subResourceIntegrity稳定化后的顶层替代项(packages/react-router-dev/CHANGELOG.md)。
export default { subResourceIntegrity: true, } satisfies Config;配置解析与合并的整体流程
结合前面的源码线索,可以把配置的生命周期归纳为一条清晰链路,便于理解各选项最终如何作用于构建:
- 定位:开发 / 构建工具在项目根目录查找
react-router.config.ts(config.ts)。 - 加载与校验:
resolveConfig加载模块,校验其具备 default export;若提供校验函数则一并执行(config.ts)。 - 应用预设:执行
presets中每个 preset 的reactRouterConfig钩子,得到一份去除了presets键的配置片段。 - 合并:按“预设片段 → 用户配置”的顺序用
mergeReactRouterConfig深度合并(同名键用户优先),随后补充各默认值(如appDirectory、ssr、routeDiscovery、serverBuildFile、serverModuleFormat、splitRouteModules、subResourceIntegrity、allowedActionOrigins等,见 config.ts)。 - 冻结与回调:得到不可变的
ResolvedReactRouterConfig,再触发各 preset 的reactRouterConfigResolved回调。 - 生效:该解析结果随构建工具注入到后续的 SSR、预渲染、路由清单与产物输出等各阶段;开发期间对配置文件的任何改动都会触发上述流程重新执行。
实际项目形态速查
在仓库的 playground 与 integration 中,可找到覆盖不同场景的真实配置范例,供你对照参考:
| 形态 | 配置示例 | 关键配置 |
|---|---|---|
| 常规框架模式 | playground/framework/react-router.config.ts | SRI + 依赖优化 |
| 纯 SPA 部署 | playground/framework-spa/react-router.config.ts | ssr: false |
| RSC + SPA | playground/rsc-vite-framework/react-router.config.ts | ssr: false+prerender |
| 静态预渲染 | playground/vite-plugin-cloudflare/react-router.config.ts | prerender: ["/static"] |
| 拆分路由模块的 SPA | playground/split-route-modules-spa/react-router.config.ts | ssr: false+splitRouteModules |
小结
react-router.config.ts用极少的配置面覆盖了 React Router 框架模式最关键的构建决策:渲染方式(ssr)、产出目录(appDirectory/buildDirectory/serverBuildFile)、模块格式(serverModuleFormat)、构建期预渲染(prerender)、运行时安全(allowedActionOrigins)、客户端路由加载策略(routeDiscovery)、多服务端 bundle 拆分(serverBundles),以及通过future、presets、buildEnd提供的扩展与演进能力。结合 config/config.ts 的源码理解其解析与合并过程,你能更有把握地为项目挑选正确的选项组合。
【免费下载链接】react-routerDeclarative routing for React项目地址: https://gitcode.com/GitHub_Trending/re/react-router
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考