概念
在基于文件系统路由(例如 Next.js、Remix 等前端框架)的项目中,我们可以根据文件和文件夹的命名规则来自动生成路由。主要会用到以下几类文件:
页面级组件
- 页面级组件是指以
index.tsx
、[id].tsx
或者[...slug].tsx
等形式命名的文件。 - 每个页面级组件对应一个具体的 URL 路径,当用户访问该路径时,对应的页面级组件会被渲染。
例如:
pages/
├─ index.tsx // 首页
├─ about/
│ └─ index.tsx // /about 页面
└─ post/
│ ├─ [id].tsx // /post/123
└─ project
└─ [...slug].tsx // /project/any/number/of/paths
布局级组件
- 布局级组件是指以
layout.tsx
命名的文件。 - 布局级组件可以对该文件所在目录及其子孙目录中的所有页面进行统一布局或样式处理(例如应用头部、底部、公共导航栏等)。
例如:
src/
├─ pages/
│ ├─ layout.tsx // 整个应用的公共布局
│ ├─ about/
│ │ ├─ layout.tsx // /about 及子路由的公共布局
│ │ └─ page.tsx // /about 页面
│ ├─ post/
│ │ ├─ layout.tsx // /post 及子路由的公共布局
│ │ ├─ page.tsx // /post 页面
│ │ └─ [id]/
│ │ └─ page.tsx // /post/[id] 页面
路由组
- 路由组通常是指只包含
layout.tsx
、loading.tsx
、error.tsx
等文件(可能还包括一些配置文件),但不包含页面级组件(index.tsx
、[id].tsx
等)的文件夹。 - 由于缺少页面级组件,路由组本身并不会生成一个直接可访问的路由,它的主要作用是为子路由提供公共布局、异步处理逻辑或将多个相似路由进行分组管理。
例如:
src/
├─ pages/
│ ├─ (base)/
│ │ ├─ layout.tsx // 提供给子路由的布局
│ │ ├─ loading.tsx // 子路由加载时显示的内容
│ │ └─ error.tsx // 子路由报错时显示的内容
│ │ └─ user/
│ │ └─ index.tsx // 此处才是真正的页面级组件
│ └─ about/
│ └─ index.tsx
└─ ...
在上面的示例中,(base)
文件夹里只有布局、加载、错误处理文件,没有 index.tsx
等页面组件,因此 (base)
仅作为“路由组”,为 user
等子路由提供公共布局或异步处理逻辑,本身不会生成 /base
路径。
忽略文件夹的聚合路由
在某些项目中,还会使用“忽略文件夹”的概念。当文件夹名称以 _
(下划线)开头时,该文件夹会被“忽略”,不会出现在 URL 路径中;其下的页面和逻辑会“聚合”到上一级路由当中。这使得我们可以更好地管理一些不希望单独生成路径层级的功能或页面。
比如下面的文件结构:
pages/
├── _error/
│ ├── 403/
│ │ └── index.tsx
│ ├── 404/
│ │ └── index.tsx
│ ├── 500/
│ │ └── index.tsx
_error
文件夹会被忽略,不会生成/error
路由层级。- 它内部的
403
、404
、500
等页面则会被聚合或提升到上一层,可能在需要时由其他机制捕获并跳转至对应页面。 - 这样做的好处是,你可以将“错误页面”或一些“工具性功能”集中到
_error
文件夹中进行管理,避免在访问路径中多出无意义的路由层级。
不同的框架或自动路由插件实现可能略有差异,具体要参考其官方文档。 但通用思路都是:以
_
开头的文件夹不会生成对应的 URL 路径,而是把子内容聚合到上层。
忽略文件夹的聚合路由和路由组的区别
忽略聚合文件与路由组都不会直接生成可访问的 URL 路径,但它们存在本质区别,主要体现在以下两点:
-
功能作用
- 路由组(通常只包含
layout.tsx
、loading.tsx
、error.tsx
等文件):- 虽然自身不生成页面,但为下级子路由提供“公共布局、加载、错误处理”等逻辑。
- 一旦子路由访问时,该路由组会被自动注入到路由层级中,实现更精细的布局或状态管理。
- 忽略聚合文件(文件夹名称以
_
开头):- 仅仅是为了“代码管理、文件分组”之用,把多个相似或关联的页面文件放在一起,方便团队协作或模块化管理。
- 该文件夹没有任何额外功能,也不会注入布局或异步处理逻辑;它不匹配任何路由、不会为子文件提供上下文环境。
- 其中的页面文件会被自动提升(或“聚合”)到上级目录的路由体系内,好比该文件夹“不可见”一样。
- 路由组(通常只包含
-
对路由产生的影响
- 路由组尽管本身不生成可访问路径,但它确实影响子路由的布局、加载、错误处理(或其他公共逻辑)。
- 忽略聚合文件对路由完全没有功能性影响,内部页面仅被视为在上级路由文件夹中的一部分;该文件夹名称也不会出现在最终路由路径里。
简而言之,路由组是“逻辑层面”的分组,可以为子路由提供布局、异步状态等;而忽略聚合文件是“纯管理层面”的分组,不会产生路由或额外逻辑,仅用于将子文件聚合到上级路由方便组织代码。
异步状态组件:loading.tsx
- 某些框架(如 Next.js 13)中,可以在路由或者布局同级目录下放置一个
loading.tsx
文件。 - 当该路由或其子路由处于“加载中”状态(比如在获取数据或等待网络请求时),就会渲染此
loading.tsx
。 - 覆盖与传递性: 如果在子路由中没有定义专门的
loading.tsx
,就会回退到使用父级或更外层的loading.tsx
;若子路由和父路由都定义了loading.tsx
,一般情况下以子路由的加载状态为准,但若子路由尚未开始渲染,就会先显示父级的loading.tsx
。
错误边界组件:error.tsx
- 同样,某些框架中也允许在路由或布局同级目录下放置一个
error.tsx
文件,用来处理渲染过程中出现的错误。 - 一般来说,错误会先被最近的(就近)错误边界所捕获;如果该路由/布局未定义
error.tsx
,则错误会向上冒泡,继续查找父级或全局的错误边界。 - 若在最内层定义了
error.tsx
,一旦发生错误,会先渲染内层的error.tsx
;如果内层没定义,就会由外层捕获并渲染外层的error.tsx
。
简而言之:
loading.tsx
通常表现为自外向内回退,若内层未定义则使用外层。error.tsx
则表现为自内向外冒泡,最内层定义的优先处理错误。
手动创建路由
如果你需要手动创建路由文件,或者使用的框架并不提供自动路由,你需要遵循以下通用规则(以常见的基于文件系统路由的框架为例):
- 文件夹名称为路由路径片段。
- 文件夹下的
index.tsx
、[id].tsx
、[...slug].tsx
等形式的文件,会被识别为页面级组件。 - 如果需要自定义布局,可以在相应文件夹中添加
layout.tsx
文件来提供该层级的公共布局。 - 如果你需要在加载或错误时做特殊处理,可以在相应文件夹中添加
loading.tsx
和/或error.tsx
文件。 - 若某个文件夹只包含
layout.tsx
、loading.tsx
、error.tsx
(以及其他配置或脚本文件)而不包含页面级组件,则被视为路由组,不生成可直接访问的路由路径,而是为其子路由提供分组和公共逻辑。 - 若文件夹名称以下划线
_
开头(如_error
),则该文件夹会被忽略并将其子内容聚合到上层路由结构中;不会单独生成 URL 路径。
举个例子
src/
├─ pages/
│ ├─ index.tsx // 访问 / 时的首页路由
│ ├─ blog/
│ │ ├─ index.tsx // 访问 /blog 时的博客首页
│ │ ├─ [id].tsx // 动态路由,访问 /blog/123
│ │ ├─ loading.tsx // /blog 下的数据加载指示
│ │ └─ error.tsx // /blog 下渲染错误时的处理
│ └─ about/
│ └─ index.tsx // 访问 /about 时的页面
└─ ...
- 当你访问
/
时,会加载pages/index.tsx
。 - 当你访问
/blog
时,会加载pages/blog/index.tsx
。在数据加载时,若需要显示加载状态,则渲染pages/blog/loading.tsx
;若发生渲染错误,则渲染pages/blog/error.tsx
。 - 当你访问
/blog/123
时,会加载pages/blog/[id].tsx
,其中id
的值为123
。 - 当你访问
/about
时,会加载pages/about/index.tsx
。
注意事项
- 动态路由命名惯例:
[id].tsx
表示单个可变路由片段(例如/blog/123
)。[...slug].tsx
表示包含 1 个或多个路由片段(例如/post/any/thing/goes/here
)。
- 多个布局级文件可以相互嵌套。一个最外层的
layout.tsx
可以包含整个网站的公共部分,然后在子路由文件夹中可以有更局部的布局文件来覆盖或扩展外层布局。 loading.tsx
与error.tsx
的范围:loading.tsx
多数情况下自外向内回退:若内层无定义,就用外层;一旦内层有定义,则优先渲染内层。error.tsx
自内向外冒泡:最内层定义的优先捕获错误,否则交给外层处理。
- 路由组:
- 若一个文件夹内没有任何页面级组件,而只包含
layout.tsx
、loading.tsx
、error.tsx
等文件,则被视为“路由组”,不会生成可直接访问的路由地址。
- 若一个文件夹内没有任何页面级组件,而只包含
- 忽略文件夹:
- 以
_
开头的文件夹不会生成路由地址,其下文件会被提升或聚合到上层路由结构中。
- 以
总结
- 在基于文件系统的路由中,文件夹名称会直接映射到路由路径,但以
_
开头或仅包含辅助文件(layout.tsx
、loading.tsx
、error.tsx
)的文件夹除外。 - 使用
index.tsx
或动态命名([id].tsx
/[...slug].tsx
)可创建页面级组件。 - 在同级放置
layout.tsx
文件,即可为该路由及其子路由提供布局级组件。 - 若需显示加载状态或捕获错误,可添加
loading.tsx
和error.tsx
:loading.tsx
在子路由未单独定义时回退到外层。error.tsx
从里向外逐级冒泡捕获错误。
- 路由组 用来对下级路由进行分组或提供公共逻辑,不直接生成访问路径;忽略文件夹 以
_
开头,其子内容会被聚合至上层。 - 根据需要进行合理的层级划分,使项目结构清晰易维护,也使路由管理更为直观高效。
Last updated on