Remix Flat Routes
此包允许您使用flat-routes
约定定义路由。这基于Ryan Florence的gist
💡 React Router v7 支持
React Router v7 使用新的路由配置。为了简化从 Remix 的迁移,团队发布了一个适配器包,该包将转换现有的基于文件的 Remix 路由到新的配置格式。
要使用您现有的基于文件的路由,请安装适配器并更新routes.ts
以包装您的适配器。
npm install -D @react-router/remix-config-routes-adapter
npm install -D remix-flat-routes
// app/routes.ts
import { remixConfigRoutes } from "@react-router/remix-config-routes-adapter";
import { flatRoutes } from "remix-flat-routes";
export const routes = remixConfigRoutes((defineRoutes) => {
return flatRoutes("routes", defineRoutes, {/* options */});
});
✨🎉 v0.5.0 中的新功能
Remix v2 flat routes 约定
remix-flat-routes
是 flat-routes 规范的初始实现。我根据用户反馈添加了一些增强功能。当 Remix v2 将 flat-routes 约定作为默认约定添加时,他们只使用了原始规范。
如果您想要诸如混合路由、扩展路由文件名、自定义参数前缀等增强功能,则需要继续使用此包。
remix-flat-routes
将始终保持与默认 Remix 约定的兼容性。此包仅仅是核心约定的超集/扩展。
注意:流行的项目,例如 Kent C. Dodds 的 Epic Stack 使用
remix-flat-routes
混合路由
您现在可以为路由名称使用嵌套文件夹,但仍保留 flat routes 的协同定位功能。
如果您有一个大型应用程序,则嵌套多层路由并不罕见。使用默认的 flat routes,文件夹名称是整个路由路径:some.really.long.route.edit/index.tsx
通常您可能有多个父布局,例如_public
或admin
。无需在每个路由中重复名称,您可以创建顶级文件夹,然后在其下嵌套路由。这样您仍然可以利用 flat 文件夹的协同定位优势。
之前
❯ tree app/routes-folders
app/routes-folders
├── _index
│ └── page.tsx
├── _public
│ └── _layout.tsx
├── _public.about
│ └── index.tsx
├── _public.contact[.jpg]
│ └── index.tsx
├── test.$
│ ├── _route.server.tsx
│ └── _route.tsx
├── users
│ ├── _layout.tsx
│ └── users.css
├── users.$userId
│ ├── _route.tsx
│ └── avatar.png
├── users.$userId_.edit
│ └── _route.tsx
└── users._index
└── index.tsx
之后
❯ tree app/routes-hybrid
app/routes-hybrid
├── _index
│ └── index.tsx
├── _public
│ ├── _layout.tsx
│ ├── about
│ │ └── _route.tsx
│ └── contact[.jpg]
│ └── _route.tsx
├── test.$
│ └── _route.tsx
└── users
├── $userId
│ ├── _route.tsx
│ └── avatar.png
├── $userId_.edit
│ └── _route.tsx
├── _index
│ └── index.tsx
├── _layout.tsx
└── users.css
flat-files
约定的嵌套文件夹(✨ v0.5.1 中的新功能)
带要创建一个文件夹但将其视为平面文件,只需将+
追加到文件夹名称。
_auth+/forgot-password.tsx => _auth.forgot-password.tsx
注意:您可以在文件夹内包含_layout.tsx文件。您不需要_public.tsx或users.tsx文件。
您仍然可以使用 flat 文件夹进行协同定位。因此,这是两种格式的最佳选择。
❯ tree app/routes-hybrid-files/
app/routes-hybrid-files/
├── _auth+
│ ├── forgot-password.tsx
│ └── login.tsx
├── _public+
│ ├── _layout.tsx
│ ├── about.tsx
│ ├── contact[.jpg].tsx
│ └── index.tsx
├── project+
│ ├── _layout.tsx
│ ├── parent.child
│ │ └── index.tsx
│ └── parent.child.grandchild
│ ├── index.tsx
│ └── styles.css
└── users+
├── $userId.tsx
├── $userId_.edit.tsx
├── _layout.tsx
└── index.tsx
<Routes>
<Route file="root.tsx">
<Route
path="forgot-password"
file="routes-hybrid-files/_auth+/forgot-password.tsx"
/>
<Route path="login" file="routes-hybrid-files/_auth+/login.tsx" />
<Route file="routes-hybrid-files/_public+/_layout.tsx">
<Route path="about" file="routes-hybrid-files/_public+/about.tsx" />
<Route
path="contact.jpg"
file="routes-hybrid-files/_public+/contact[.jpg].tsx"
/>
<Route index file="routes-hybrid-files/_public+/index.tsx" />
</Route>
<Route path="project" file="routes-hybrid-files/project+/_layout.tsx">
<Route
path="parent/child"
file="routes-hybrid-files/project+/parent.child/index.tsx"
>
<Route
path="grandchild"
file="routes-hybrid-files/project+/parent.child.grandchild/index.tsx"
/>
</Route>
</Route>
<Route path="users" file="routes-hybrid-files/users+/_layout.tsx">
<Route path=":userId" file="routes-hybrid-files/users+/$userId.tsx" />
<Route
path=":userId/edit"
file="routes-hybrid-files/users+/$userId_.edit.tsx"
/>
<Route index file="routes-hybrid-files/users+/index.tsx" />
</Route>
</Route>
</Routes>
扩展路由文件名
除了标准的index | route | page | layout
名称外,任何具有_
前缀的文件都将被视为路由文件。这将使查找特定路由变得更容易,而不是浏览一堆route.tsx
文件。这受到SolidStart“重命名索引”功能的启发。
所以不是
_public.about/route.tsx
_public.contact/route.tsx
_public.privacy/route.tsx
您可以将它们命名为
_public.about/_about.tsx
_public.contact/_contact.tsx
_public.privacy/_privacy.tsx
多个路由文件夹
您现在可以传递除默认routes
文件夹之外的其他路由文件夹。这些路由将合并到单个命名空间中,因此您可以将一个文件夹中的路由与另一个文件夹中的共享路由一起使用。
自定义参数前缀
您可以覆盖默认的参数前缀$
。某些 shell 使用$
前缀表示变量,这可能会导致 shell 扩展问题。使用任何有效的文件名字符,例如:^
users.^userId.tsx => users/:userId
test.^.tsx => test/*
自定义基本路径
您可以覆盖默认的基本路径/
。这将把您的基本路径附加到根路径。
可选路由段
React Router 将引入一个用于可选路由段的新功能。要在 flat routes 中使用可选段,只需将路由名称括在()
中。
parent.(optional).tsx => parent/optional?
自定义应用程序目录
您可以覆盖默认的应用程序目录app
。
🛠 安装
npm install -D remix-flat-routes
⚙️ 配置
更新您的remix.config.js文件并使用自定义路由配置选项。
const { flatRoutes } = require('remix-flat-routes')
/**
* @type {import("@remix-run/dev").AppConfig}
*/
module.exports = {
// ignore all files in routes folder to prevent
// default remix convention from picking up routes
ignoredRouteFiles: ['**/*'],
routes: async defineRoutes => {
return flatRoutes('routes', defineRoutes)
},
}
API
function flatRoutes(
routeDir: string | string[],
defineRoutes: DefineRoutesFunction,
options: FlatRoutesOptions,
)
type FlatRoutesOptions = {
appDir?: string // optional app directory (defaults to app)
basePath?: string // optional base path (default is '/')
paramPrefixChar?: string // optional param prefix (default is '$')
ignoredRouteFiles?: string[] // optional files to ingore as routes (same as Remix config option)
visitFiles?: VisitFilesFunction // optional visitor (useful for tests to provide files without file system)
}
注意:routeDir
应相对于app
文件夹。如果您想使用routes
文件夹,则需要更新ignoredRouteFiles
属性以忽略**所有**文件:**/*
🔨 Flat Routes 约定
示例(flat-files)
routes/
_auth.forgot-password.tsx
_auth.login.tsx
_auth.reset-password.tsx
_auth.signup.tsx
_auth.tsx
_landing.about.tsx
_landing.index.tsx
_landing.tsx
app.calendar.$day.tsx
app.calendar.index.tsx
app.calendar.tsx
app.projects.$id.tsx
app.projects.tsx
app.tsx
app_.projects.$id.roadmap.tsx
app_.projects.$id.roadmap[.pdf].tsx
作为 React Router 路由
<Routes>
<Route element={<Auth />}>
<Route path="forgot-password" element={<Forgot />} />
<Route path="login" element={<Login />} />
<Route path="reset-password" element={<Reset />} />
<Route path="signup" element={<Signup />} />
</Route>
<Route element={<Landing />}>
<Route path="about" element={<About />} />
<Route index element={<Index />} />
</Route>
<Route path="app" element={<App />}>
<Route path="calendar" element={<Calendar />}>
<Route path=":day" element={<Day />} />
<Route index element={<CalendarIndex />} />
</Route>
<Route path="projects" element={<Projects />}>
<Route path=":id" element={<Project />} />
</Route>
</Route>
<Route path="app/projects/:id/roadmap" element={<Roadmap />} />
<Route path="app/projects/:id/roadmap.pdf" />
</Routes>
单独解释
文件名 | URL | 嵌套在…中 |
---|---|---|
_auth.forgot-password.tsx |
/forgot-password |
_auth.tsx |
_auth.login.tsx |
/login |
_auth.tsx |
_auth.reset-password.tsx |
/reset-password |
_auth.tsx |
_auth.signup.tsx |
/signup |
_auth.tsx |
_auth.tsx |
n/a | root.tsx |
_landing.about.tsx |
/about |
_landing.tsx |
_landing.index.tsx |
/ |
_landing.tsx |
_landing.tsx |
n/a | root.tsx |
app.calendar.$day.tsx |
/app/calendar/:day |
app.calendar.tsx |
app.calendar.index.tsx |
/app/calendar |
app.calendar.tsx |
app.projects.$id.tsx |
/app/projects/:id |
app.projects.tsx |
app.projects.tsx |
/app/projects |
app.tsx |
app.tsx |
/app |
root.tsx |
app_.projects.$id.roadmap.tsx |
/app/projects/:id/roadmap |
root.tsx |
app_.projects.$id.roadmap[.pdf].tsx |
/app/projects/:id/roadmap.pdf |
n/a (资源路由) |
嵌套布局
默认匹配
默认情况下,flat-routes
会将当前路由嵌套到具有最长匹配前缀的父布局中。
给定布局路由app.calendar.tsx
,以下路由将嵌套在app.calendar.tsx
下,因为app.calendar
是最长匹配前缀。
app.calendar.index.tsx
app.calendar.$day.tsx
覆盖匹配
有时您希望使用路由层次结构中较高级别的父布局。使用默认的 Remix 约定,您将使用点 (.
) 表示法而不是嵌套文件夹。使用flat-routes
,由于路由文件始终使用点,因此存在不同的约定来指定要嵌套到的布局。
假设您有一个app.tsx
布局,并且您有一个路由不想与该布局共享,而是想与root.tsx
匹配。要覆盖默认的父匹配,请将尾部下划线 (_
) 附加到要嵌套到的路由的直接子级段。
app_.projects.$id.roadmap.tsx
将嵌套在root
下,因为没有匹配的路由
- ❌
app_.projects.$id.tsx
- ❌
app_.projects.tsx
- ❌
app_.tsx
- ✅
root.tsx
约定
文件名 | 约定 | 行为 |
---|---|---|
privacy.jsx |
文件名 | 普通路由 |
pages.tos.jsx |
带点的无布局 | 普通路由,. -> / |
about.jsx |
带有子元素的文件名 | 父布局路由 |
about.contact.jsx |
点 | 布局的子路由 |
about.index.jsx |
索引文件名 | 布局的索引路由 |
about._index.jsx |
index.tsx 的别名 | 布局的索引路由* |
about_.company.jsx |
尾部下划线 | URL 段,无布局 |
app_.projects.$id.roadmap.tsx |
尾部下划线 | 更改默认父布局 |
_auth.jsx |
前导下划线 | 布局嵌套,无 URL 段 |
_auth.login.jsx |
前导下划线 | 无路径布局路由的子元素 |
users.$userId.jsx |
前导 $ | URL 参数 |
docs.$.jsx |
裸 $ | 通配符路由 |
dashboard.route.jsx |
route 后缀 | 可选,完全忽略 |
investors/[index].jsx |
方括号 | 转义常规字符 |
注意:索引路由的前导下划线是可选的,但有助于将文件排序到目录列表的顶部。
理由
-
使查看应用程序定义的路由变得更容易 - 只需打开“routes/”,它们就都在那里。由于文件系统通常先排序文件夹,因此当您有数十个路由时,今天很难看到哪些文件夹具有布局,哪些没有。现在所有相关的路由都一起排序。
-
减少重构/重新设计摩擦 - 虽然代码编辑器在您移动文件时非常擅长修复导入,并且 Remix 具有
"~"
导入别名,但一般来说,重构没有大量嵌套文件夹的代码库更容易。Remix 将不再强制执行此操作。此外,在重新设计用户界面时,调整文件名称比创建/删除文件夹和移动路由以更改其嵌套方式更简单。
-
帮助应用迁移到 Remix - 现有的应用通常没有像今天约定那样嵌套的路由文件夹结构。迁移到 Remix 非常繁琐,因为您必须处理所有导入。
同址
虽然示例仅限于文件,但它们实际上只是“导入路径”。因此,您可以为路由创建一个文件夹,并导入index
文件,从而允许路由的所有模块并排存在。这称为扁平文件夹约定,而不是上面详细介绍的扁平文件约定。
示例(扁平文件夹)
routes/
_auth.forgot-password.tsx
_auth.login.tsx
_auth.tsx
_landing.about.tsx
_landing.index.tsx
_landing.tsx
app.projects.tsx
app.projects.$id.tsx
app.tsx
app_.projects.$id.roadmap.tsx
每个路由都成为一个文件夹,其名称为路由名称减去文件扩展名。然后路由文件命名为index.tsx。
所以app.projects.tsx 变为 app.projects/index.tsx
routes/
_auth/
index.tsx x <- route file (same as _auth.tsx)
_auth.forgot-password/
index.tsx <- route file (same as _auth.forgot-password.tsx)
_auth.login/
index.tsx <- route files (same as _auth.login.tsx)
_landing.about/
index.tsx <- route file (same as _landing.about.tsx)
employee-profile-card.tsx
get-employee-data.server.tsx
team-photo.jpg
_landing.index/
index.tsx <- route file (same as _landing.index.tsx)
scroll-experience.tsx
_landing/
index.tsx <- route file (same as _landing.tsx)
header.tsx
footer.tsx
app/
index.tsx <- route file (same as app.tsx)
primary-nav.tsx
footer.tsx
app_.projects.$id.roadmap/
index.tsx <- route file (same as app_.projects.$id.roadmap.tsx)
chart.tsx
update-timeline.server.tsx
app.projects/
index.tsx <- layout file (sames as app.projects.tsx)
project-card.tsx
get-projects.server.tsx
project-buttons.tsx
app.projects.$id/
index.tsx <- route file (sames as app.projects.$id.tsx)
别名
由于路由文件现在命名为index.tsx,并且您可以在同一路由文件夹中并置其他文件,因此index.tsx文件可能会在文件列表中丢失。您还可以使用以下别名表示index.tsx。下划线前缀会将文件排序到目录列表的顶部。
_index.tsx
_layout.tsx
_route.tsx
注意:_layout.tsx和_route.tsx文件只是更明确地说明了它们的作用。它们的工作方式与index.tsx相同。
与扁平文件一样,索引路由(不要与索引路由文件混淆)也可以使用下划线前缀。路由_landing.index
可以保存为_landing.index/index.tsx
或_landing._index/_index.tsx
。
这有点更武断,但我认为这最终是大多数开发人员想要的。每个路由都成为一个包含所有依赖项的“迷你应用”。使用ignoredRouteFiles
选项,完全不清楚哪些文件是路由,哪些不是。
🚚 迁移现有路由
您现在可以将现有的路由迁移到新的flat-routes
约定。只需运行
npx migrate-flat-routes <sourceDir> <targetDir> [options]
Example:
npx migrate-flat-routes ./app/routes ./app/flatroutes --convention=flat-folders
NOTE:
sourceDir and targetDir are relative to project root
Options:
--convention=<convention>
The convention to use when migrating.
flat-files - Migrates to flat files
flat-folders - Migrates to flat directories with route.tsx files
hybrid - Keep folder structure with '+' suffix and _layout files
--force
Overwrite target directory if it exists
😍 贡献者
感谢这些很棒的人(表情符号键)
Kiliman 💻 📖 |
Ryan Florence 📖 |
Brandon Pittman 📖 💻 |
Mehdi Achour 📖 |
Fidel González 📖 |
Andrew Haines 💻 |
Wonu Lee 💻 |
Markus Wolf 💻 |
Sarat Chandra Balla 💻 |
此项目遵循all-contributors规范。欢迎任何形式的贡献!