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 获取。