Modern.js 的路由基于 React Router 6,提供了基于文件约定的路由能力,并支持了业界流行的约定式路由模式:嵌套路由。当入口被识别为 约定式路由 时,Modern.js 会自动基于文件系统,生成对应的路由结构。
本小节提到的路由,都是约定式路由。
嵌套路由是一种将 URL 分段与组件层次结构和数据耦合起来的路由模式。通常,URL 段会决定:
因此,使用嵌套路由时,页面的路由与 UI 结构是相呼应的,我们将会详细介绍这种路由模式。
在 routes/
目录下,子目录名会作为路由 URL 的映射,Modern.js 有两个文件约定 layout.tsx
和 page.tsx
。这两个文件决定了应用的布局层次,其中:
page.tsx
为内容组件,所在目录下存在该文件时,对应的路由 URL 可访问。layout.tsx
为布局组件,控制所在目录下所有子路由的布局,使用 <Outlet>
表示子组件。
.ts
、.js
、.jsx
或 .tsx
文件扩展名可用于上述约定文件。
<Page>
组件是指 routes/
目录下所有 page.tsx
文件,也是所有路由的叶子组件。除了通配路由外,任何路由都应该由 <Page>
组件结束。
当应用中存在以下目录结构时:
会产出下面两条路由:
/
/user
<Layout>
组件是指 routes/
目录下所有 layout.tsx
文件,它们表示对应路由片段的布局,使用 <Outlet>
表示子组件。
<Outlet>
是 React Router 6 中提供的 API,详情可以查看 Outlet。
不同目录结构下,<Outlet>
所代表的组件也不同。为了方便介绍 <Layout>
与 <Outlet>
的关系,以下面的文件目录举例:
/
时,routes/layout.tsx
中的 <Outlet>
代表的是 routes/page.tsx
中导出的组件,路由的 UI 结构为:/blog
时,routes/layout.tsx
中的 <Outlet>
代表的是 routes/blog/page.tsx
中导出的组件,路由的 UI 结构为:/user
时,routes/layout.tsx
中的 <Outlet>
代表的是 routes/user/layout.tsx
中导出的组件。routes/user/layout.tsx
中的 <Outlet>
代表的是 routes/user/page.tsx
中导出的组件,路由的 UI 结构为:总结而言,如果子路由的文件目录下存在 layout.tsx
,上一级 layout.tsx
中的 <Outlet>
即为子路由文件目录下的 layout.tsx
,否则为子路由文件目录下的 page.tsx
。
通过 []
命名的文件目录,生成的路由会作为动态路由。例如以下文件目录:
routes/[id]/page.tsx
文件会转为 /:id
路由。除了可以确切匹配的 /blog
路由,其他所有 /xxx
都会匹配到该路由。
在组件中,可以通过 useParams 获取对应命名的参数。
通过 [$]
命名的文件目录,生成的路由会作为动态可选路由。例如以下文件目录:
routes/blog/[id$]/page.tsx
文件会转为 /blog/:id?
路由。/blog
下的所有路由都会匹配到该路由,并且 id
参数可选存在。通常在区分创建与编辑时,可以使用该路由。
如果在某个子目录下存在 $.tsx
文件,该文件会作为通配路由组件,当没有匹配的路由时,会渲染该路由组件。
$.tsx
可以认为是一种特殊的 <Page>
组件,如果路由无法匹配,则 $.tsx
会作为 <Layout>
的子组件渲染。
如果当前目录下不存在 <Layout>
组件时,则 $.tsx
不会生效。
例如以下目录结构:
当你访问 /blog/a
时,无法匹配到任意路由,则页面会渲染 routes/blog/$.tsx
组件,路由的 UI 结构为:
如果希望访问 /blog
时,也匹配到 blog/$.tsx
文件,需要删除同目录下的 blog/layout.tsx
文件,同时保证 blog
下面没有其他子路由。
同样,$.tsx
中可以使用 useParams 捕获 url 的剩余部分。
通配路由可以被添加到 routes/
目录下的任意子目录中,一种常见的使用场景是通过 $.tsx
文件去定制任意层级的 404 内容。
例如你需要对所有未匹配到的路由,都展示一个 404 页面,可以添加 routes/$.tsx
文件:
此时,当访问除了 /
或 /blog/*
以外的路由时,都会匹配到 routes/$.tsx
组件,展示 404 页面。
某些场景下,每个路由会拥有属于自己的数据,应用需要在其他组件中获取匹配到的路由的这些数据。一个常见的例子是在布局中获取到匹配路由的面包屑信息。
Modern.js 提供了独立的约定,每个 Layout
, $
或 Page
文件都可以定义一个自己的 config
文件,如 page.config.ts
,该文件中我们约定了一个具名导出 handle
,这个字段中你可以定义任意属性:
定义的这些属性可以通过 useMatches
hook 获取。