资讯动态

TanStack Router 文件命名约定全解析:从 `__root.tsx` 到 `$`、`_`、`-`、`()`、`[x]` 的路由生成规则

发布时间:2026/9/14 14:53:43 来源:尧图企业网站定制
TanStack Router 文件命名约定全解析从__root.tsx到$、_、-、()、[x]的路由生成规则【免费下载链接】router A client-first, server-capable, fully type-safe router and full-stack framework for the web (React and more).项目地址: https://gitcode.com/GitHub_Trending/ro/router本指南系统讲解 TanStack Router 文件式路由File-Based Routing必须遵循的文件命名约定覆盖__root.tsx根路由、.嵌套分隔符、$动态参数、_前缀/后缀、-排除前缀、(folder)路由组、[x]转义、index/route令牌及.route.tsx约定。读完本文你将能准确预测任意一组路由文件会被生成成怎样的路由树与组件树并能在实际项目中通过tsr.config.json定制这些约定。为什么文件命名约定如此重要TanStack Router 采用客户端优先、服务端可用、全类型安全的路由设计。在文件式路由模式下路由结构不是用代码声明的而是由src/routes目录下的文件名与目录名直接推导出来的。其核心流程是路由器插件/CLI 扫描路由目录 → 依据命名约定解析出每条路由的routePath→ 生成routeTree.gen.ts→ 运行时将 URL 与嵌套路由树匹配渲染出对应的组件树。因此怎么给文件命名直接决定了URL 是什么、组件怎么嵌套、类型如何推导。文件命名约定正是这套机制的地基相关概念在 Route Trees 指南 中有更详细的阐述。全部约定速查表如下来自 file-naming-conventions.md特性说明__root.tsx根路由文件必须命名为__root.tsx且必须放在配置的routesDirectory根目录下.分隔符路由可用.字符表示嵌套路由例如blog.post将生成为blog的子路由$令牌含$令牌的路由段是参数化的会从 URL pathname 中提取值作为路由param_前缀以_前缀开头的路由段被视为无路径布局路由pathless layout匹配子路由时不会占用 URL pathname_后缀以_后缀结尾的路由段将从父路由中解除嵌套-前缀以-前缀开头的文件与文件夹会被排除在路由树之外不会加入routeTree.gen.ts可用于在路由文件夹中就近存放逻辑代码(folder)文件夹命名模式匹配此模式的文件夹被视为路由组route group其文件夹名不会进入路由的 URL 路径[x]转义方括号可转义文件名中本会具有路由含义的特殊字符例如script[.]js.tsx变为/script.jsapi[.]v1.tsx变为/api.v1index令牌以index令牌结尾在任何文件扩展名之前的路由段在 URL pathname 精确匹配父路由时匹配父路由。可通过indexToken配置项自定义支持字符串与正则见 options.route.tsx文件类型用目录组织路由时可用route后缀在目录路径处创建路由文件例如blog.post.route.tsx或blog/post/route.tsx均可作为/blog/post路由的路由文件。可通过routeToken配置项自定义支持字符串与正则见 options 记住项目的文件命名约定可能受到所配置 options 的影响。__root.tsx一切路由的根根路由是整个路由树最顶层的路由封装了其余所有路由作为其子路由。它没有路径、始终被匹配、组件始终被渲染同时仍拥有与其他路由完全相同的能力组件、loader、search 参数校验等。命名上必须遵守两条硬性规则文件必须命名为__root.tsx必须放在routesDirectory配置项指定的目录根目录默认./src/routes。在源码层面路由生成器扫描完目录后会查找routePath /__root的节点并将其强制标记为__root类型、变量名固定为root见 getRouteNodes.ts。这意味着无论你的__root.tsx在何处它最终都会成为组件树的最外层Root。根路由的创建使用createRootRoute()也可以使用createRootRouteWithContextT()注入全局上下文// src/routes/__root.tsx import { createRootRoute } from tanstack/react-router export const Route createRootRoute()// 带 Context 的根路由 import { createRootRouteWithContext } from tanstack/react-router import type { QueryClient } from tanstack/react-query export interface MyRouterContext { queryClient: QueryClient } export const Route createRootRouteWithContextMyRouterContext()更多细节可参阅 Routing Concepts - The Root Route。.分隔符与嵌套路由.字符是文件式路由引入的关键概念用文件名中的点来表示路由嵌套层级。它与目录层级是等价的允许你为少数深层嵌套路由避免创建大量目录同时继续用目录组织较宽的路由层级。例如blog.post会生成blog路由的post子路由。src/routes ├── posts.tsx # /posts ├── posts.index.tsx # /posts (精确匹配) ├── posts.$postId.tsx # /posts/$postId对应的组件树输出为RootPosts # /posts RootPostsPostsIndex # /posts (精确) RootPostsPost # /posts/$postId.,$,_,-等令牌在源码中通过预编译的正则表达式对路由段进行匹配TokenRegexBundle见 getRouteNodes.ts因此在理解命名时可以把每个.段看作一个段处理器。$令牌动态路径参数含$的路由段是参数化的它会从 URL pathname 中捕获对应位置的文本作为路由param。动态路径参数在扁平路由flat和目录路由directory中都可以使用。文件名路由路径组件输出.........posts.$postId.tsx/posts/$postIdRootPostsPost例如 URL/posts/123会匹配/posts/$postId路由params对象为{ postId: 123 }。动态段在每一层路径都生效例如/posts/$postId/$revisionId中每个$段都会被捕获进params对象。在路由文件中的完整用法loader、组件均可使用import { createFileRoute } from tanstack/react-router export const Route createFileRoute(/posts/$postId)({ loader: async ({ params }) { return fetchPost(params.postId) }, component: PostComponent, }) function PostComponent() { const { postId } Route.useParams() return divPost ID: {postId}/div }动态参数还有几个延伸形态值得掌握Splat / 通配路由路径仅为$的路由会捕获从$到 URL 末尾的任意剩余路径保存在_splat属性下。例如路由文件files.$.tsx匹配 URL/files/documents/hello-world时params为{ _splat: documents/hello-world }。可选路径参数使用{-$paramName}语法例如posts.{-$category}.tsx同时匹配/posts与/posts/tech缺省时值为undefined。前缀/后缀参数用花括号包裹参数名并放置前后缀文本例如posts/post-{$postId}.tsx匹配/posts/post-123files/{$fileName}[.]txt.tsx匹配/files/report.txt。完整的参数使用loader、beforeLoad、useParams、导航、i18n 模式参见 Path Params 指南。_前缀无路径布局路由Pathless Layout Routes以_前缀开头的路由段是无路径布局路由它们用来包裹子路由的组件与逻辑但不要求 URL 中有匹配路径。下划线之后的部分用作该路由的 ID——每个路由必须唯一可识别尤其在 TypeScript 中避免类型错误、实现有效自动补全。文件名路由路径组件输出_app.tsx_app.a.tsx/aRootAppA_app.b.tsx/bRootAppB实现上路由生成器会检查最后一个路由段是否以_开头且未被[ ]转义、不是index/route令牌从而将路由类型标记为pathless_layout见 getRouteNodes.ts。实际写法如下import { Outlet, createFileRoute } from tanstack/react-router export const Route createFileRoute(/_pathlessLayout)({ component: PathlessLayoutComponent, }) function PathlessLayoutComponent() { return ( div h1Pathless layout/h1 Outlet / /div ) }文件树与 URL 的对应关系routes/ ├── _pathlessLayout.tsx ├── _pathlessLayout.a.tsx ├── _pathlessLayout.b.tsxURL 路径组件/Index/aPathlessLayoutA/bPathlessLayoutB目录形态同样支持_pathlessLayout/route.tsxa.tsxb.tsx。但要注意无路径布局路由不支持动态路由段因为它们的匹配不基于 URL 路径段。因此_$postId/这种命名是无效的需要拆成$postId/与_postPathlessLayout/的组合。更完整的说明见 Routing Concepts - Pathless Layout Routes。_后缀非嵌套路由Non-Nested Routes以_作为父路由文件段的后缀可以解除该路由与父路由的嵌套让它渲染自己的组件树。考虑下面的扁平路由树routes/ ├── posts.tsx ├── posts.$postId.tsx ├── posts_.$postId.edit.tsxURL 路径组件/postsPosts/posts/123PostsPost postId123/posts/123/editPostEditor postId123posts.$postId.tsx正常嵌套在posts.tsx之下渲染PostsPostposts_.$postId.edit.tsx因为posts_段不与其他路由共享相同的posts前缀被视为顶层路由渲染PostEditor。这在需要编辑页不套文章列表布局等场景下非常实用。-前缀排除文件与文件夹以-前缀命名的文件与文件夹会被排除在路由树之外不会加入routeTree.gen.ts。这让你可以在路由目录中就近放置组件、hooks、工具函数等逻辑代码而不用担心它们被误解析成路由。routes/ ├── posts.tsx ├── -posts-table.tsx // 被忽略 ├── -components/ // 被忽略 │ ├── header.tsx // 被忽略 │ ├── footer.tsx │ └── ...被排除的文件可以正常被路由文件导入import { createFileRoute } from tanstack/react-router import { PostsTable } from ./-posts-table import { PostsHeader } from ./-components/header import { PostsFooter } from ./-components/footer export const Route createFileRoute(/posts)({ loader: () fetchPosts(), component: PostComponent, }) function PostComponent() { const posts Route.useLoaderData() return ( div PostsHeader / PostsTable posts{posts} / PostsFooter / /div ) }源码层面-前缀对应routeFileIgnorePrefix配置项默认值为-扫描目录时所有以该前缀开头的目录项都会被直接过滤掉见 getRouteNodes.ts 与 config.ts。(folder)路由组Route Groups匹配(folder)模式的文件夹被视为路由组它们纯粹用于组织路由文件不会进入路由的 URL 路径也不影响路由树或组件树。routes/ ├── index.tsx ├── (app)/ │ ├── dashboard.tsx │ ├── settings.tsx │ └── users.tsx ├── (auth)/ │ ├── login.tsx │ └── register.tsxURL 路径组件/Index/dashboardDashboard/settingsSettings/usersUsers/loginLogin/registerRegister可以看到(app)与(auth)目录完全没有体现在 URL 中。另外注意生成器会校验(xxx).tsx这种路由组的路由配置文件写法并直接报错disallowedRouteGroupConfiguration见 getRouteNodes.ts遇到时会提示改用 layout/pathless 路由。[x]转义让特殊字符回归字面量方括号可以转义文件名中本会具有路由含义的特殊字符。例如script[.]js.tsx→/script.jsapi[.]v1.tsx→/api.v1这条规则同样作用于index、route等令牌如果你想要一个字面量的index或route段把它包进方括号即可如[index].tsx、[route].tsx。源码中通过对比原始段与去掉方括号后的段来判断是否发生了转义被转义的段不会参与令牌匹配见 getRouteNodes.ts。结合参数前缀/后缀使用也很常见files/{$fileName}[.]txt.tsx中的[.]就是转义后的字面量点号。index令牌与.route.tsx目录式组织的两个基石index令牌路由段在文件扩展名之前以index结尾表示URL 精确匹配父路由时匹配父路由。默认令牌为index可用indexToken配置项自定义支持字符串与正则。下面两种写法在运行时等价都对应/posts/src/routes/posts.index.tsx - /posts/ src/routes/posts/index.tsx - /posts/注意在路由文件中索引路由的路径要写带尾斜杠的形式import { createFileRoute } from tanstack/react-router // 注意尾斜杠用于定位索引路由 export const Route createFileRoute(/posts/)({ component: PostsIndexComponent, }).route.tsx文件类型使用目录组织路由时route后缀可以在目录路径处创建路由文件充当该目录的布局/配置路由。默认令牌为route可用routeToken配置项自定义。以下三种写法在运行时等价都是/postssrc/routes/posts.tsx - /posts src/routes/posts.route.tsx - /posts src/routes/posts/route.tsx - /posts典型目录形态routes/ ├── app/ │ ├── route.tsx # /app 的布局路由 │ ├── dashboard.tsx # /app/dashboard │ └── settings.tsx # /app/settings更多用法如app/users/$userId/route.tsx组合布局与动态参数见 Routing Concepts - Layout Routes。用正则定制indexToken与routeTokenindexToken与routeToken都支持正则表达式方便匹配多种命名习惯。在tsr.config.jsonJSON 配置中使用带regex与可选flags的对象{ routeToken: { regex: [a-z]-layout, flags: i } }在代码内联配置中可直接使用原生RegExp{ routeToken: /[a-z]-layout/i, }使用[a-z]-layout正则时dashboard.main-layout.tsx、posts.protected-layout.tsx、admin.settings-layout.tsx都会被识别为布局路由。注意正则是对路由路径整个末段进行匹配的——dashboard.main-layout.tsx匹配main-layout是完整段而dashboard.my-layout-extra.tsx不匹配段是my-layout-extra。indexToken同理{ indexToken: { regex: [a-z]-page } }使用[a-z]-page时home-page.tsx、posts.list-page.tsx、dashboard.overview-page.tsx都会被识别为索引路由。当你想让某个段字面匹配正则令牌时同样用方括号转义例如[home-page].tsx会生成字面量/home-page。完整配置说明见 File-Based Routing API Reference。综合演练用命名约定读出一整棵路由树将上述约定组合起来就能直接读出任意路由目录的完整结构。例如 Route Trees 指南 中给出的示例目录/routes ├── __root.tsx ├── index.tsx ├── about.tsx ├── posts/ │ ├── index.tsx │ ├── $postId.tsx ├── posts.$postId.edit.tsx ├── settings/ │ ├── profile.tsx │ ├── notifications.tsx ├── _pathlessLayout/ │ ├── route-a.tsx │ ├── route-b.tsx ├── files/ │ ├── $.tsx对应的路由树为blog → posts → $postId形态的嵌套层级运行时渲染的组件树为RootPostsPost postId123 //Posts/Root这样的嵌套结构Blog Posts Post postId123 / /Posts /Blog其中_pathlessLayout/提供无路径包裹、files/$.tsx提供 splat 通配、posts.$postId.edit.tsx以扁平.方式表达$postId下的编辑子路由——这就是混合扁平与目录路由的典型应用。配置提醒与陷阱使用这些约定时有几条需要注意不要冲突不要把routeFilePrefix、routeFileIgnorePrefix、routeFileIgnorePattern配置成与文件命名约定中的任何令牌一致否则可能产生意外行为见 file-based-routing.md 的警告。默认值routeFileIgnorePrefix默认为-indexToken默认为indexrouteToken默认为routeroutesDirectory默认为./src/routesgeneratedRouteTree默认为./src/routeTree.gen.ts见 config.ts 的 zod schema 定义。生成结果以routeTree.gen.ts为准被-排除的文件不会进入生成文件__root节点会以root变量名固定在生成树顶层。想深入理解生成逻辑可阅读 router-generator 源码。不同类型的路由文件后缀.lazy.tsx、.loader.tsx、.component.tsx、.errorComponent.tsx等后缀同样参与解析旧式.component.tsx等后缀已标记弃用见 getRouteNodes.ts命名时注意避免与这些保留后缀冲突。文件式路由是 TanStack Router 推荐的首选路由方式官方文档也基本以它为视角撰写见 File-Based Routing。掌握上述命名约定你就能完全掌控路由树的生成结果并充分利用其自动代码分割与全链路类型安全能力。【免费下载链接】router A client-first, server-capable, fully type-safe router and full-stack framework for the web (React and more).项目地址: https://gitcode.com/GitHub_Trending/ro/router创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价