Remix 平面路由
此套件讓您可以使用 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 平面路由慣例
remix-flat-routes
是平面路由規範的最初實作。我根據使用者回饋添加了一些增強功能。當 Remix v2 將平面路由慣例添加為預設值時,他們僅使用了原始規範。
如果您想要諸如混合路由、擴展路由檔名、自訂參數前綴等增強功能,您將需要繼續使用此套件。
remix-flat-routes
將始終保持與預設 Remix 慣例的相容性。此套件僅是核心慣例的超集/擴展。
注意:像 Kent C. Dodds 的 Epic Stack 等熱門專案都使用了
remix-flat-routes
。
混合路由
您現在可以使用巢狀資料夾作為路由名稱,同時仍然保留平面路由的並置功能。
如果您有一個大型應用程式,路由巢狀多層並不罕見。使用預設平面路由時,資料夾名稱是整個路由路徑:some.really.long.route.edit/index.tsx
通常您可能會有幾個父版面配置,例如 _public
或 admin
。您不必在每個路由中重複名稱,而是可以建立頂層資料夾,然後將路由巢狀置於其下。這樣,您仍然可以利用平面資料夾的並置優勢。
之前
❯ 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 檔案。
您仍然可以使用平面資料夾進行並置。因此,這是兩種格式的最佳組合。
❯ 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 將為可選路由區段引入新功能。若要在平面路由中使用可選區段,只需將路由名稱包裝在 ()
中即可。
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
屬性以忽略所有檔案:**/*
🔨 平面路由慣例
範例(平面檔案)
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>
個別說明
檔名 | 網址 | 巢狀於... |
---|---|---|
_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 |
不適用 | root.tsx |
_landing.about.tsx |
/about |
_landing.tsx |
_landing.index.tsx |
/ |
_landing.tsx |
_landing.tsx |
不適用 | 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 |
不適用(資源路由) |
巢狀版面配置
預設比對
預設情況下,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 |
路由字尾 | 選用,完全忽略 |
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)
別名 (Aliases)
由於路由檔案現在被命名為 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 規範。歡迎任何形式的貢獻!