檔案路徑慣例
本頁面內容

檔案路徑慣例

@react-router/fs-routes 套件啟用基於檔案慣例的路由配置。

設定

首先安裝 @react-router/fs-routes 套件

npm i @react-router/fs-routes

然後使用它在您的 app/routes.ts 檔案中提供路由配置

import { type RouteConfig } from "@react-router/dev/routes";
import { flatRoutes } from "@react-router/fs-routes";

export default flatRoutes() satisfies RouteConfig;

預設情況下,app/routes 目錄中的任何模組都將成為應用程式中的路由。ignoredRouteFiles 選項允許您指定不應包含為路由的檔案

import { type RouteConfig } from "@react-router/dev/routes";
import { flatRoutes } from "@react-router/fs-routes";

export default flatRoutes({
  ignoredRouteFiles: ["home.tsx"],
}) satisfies RouteConfig;

這將預設在 app/routes 目錄中尋找路由,但這可以通過相對於您的應用程式目錄的 rootDirectory 選項進行配置

import { type RouteConfig } from "@react-router/dev/routes";
import { flatRoutes } from "@react-router/fs-routes";

export default flatRoutes({
  rootDirectory: "file-routes",
}) satisfies RouteConfig;

本指南的其餘部分將假設您正在使用預設的 app/routes 目錄。

基本路由

檔名映射到路由的 URL 路徑名稱,但 _index.tsx 除外,它是索引路由,適用於根路由。您可以使用 .js.jsx.ts.tsx 檔案擴展名。

app/
├── routes/
│   ├── _index.tsx
│   └── about.tsx
└── root.tsx
網址 匹配的路由
/ app/routes/_index.tsx
/about app/routes/about.tsx

請注意,由於巢狀路由,這些路由將在 app/root.tsx 的 outlet 中呈現。

點分隔符

在路由檔名中新增 . 將在 URL 中建立 /

 app/
├── routes/
│   ├── _index.tsx
│   ├── about.tsx
│   ├── concerts.trending.tsx
│   ├── concerts.salt-lake-city.tsx
│   └── concerts.san-diego.tsx
└── root.tsx
網址 匹配的路由
/ app/routes/_index.tsx
/about app/routes/about.tsx
/concerts/trending app/routes/concerts.trending.tsx
/concerts/salt-lake-city app/routes/concerts.salt-lake-city.tsx
/concerts/san-diego app/routes/concerts.san-diego.tsx

點分隔符也建立巢狀結構,請參閱巢狀結構章節以取得更多資訊。

動態區段

通常您的 URL 不是靜態的,而是資料驅動的。動態區段允許您匹配 URL 的區段,並在您的程式碼中使用該值。您可以使用 $ 字首建立它們。

 app/
├── routes/
│   ├── _index.tsx
│   ├── about.tsx
│   ├── concerts.$city.tsx
│   └── concerts.trending.tsx
└── root.tsx
網址 匹配的路由
/ app/routes/_index.tsx
/about app/routes/about.tsx
/concerts/trending app/routes/concerts.trending.tsx
/concerts/salt-lake-city app/routes/concerts.$city.tsx
/concerts/san-diego app/routes/concerts.$city.tsx

該值將從 URL 中解析,並傳遞到各種 API。我們將這些值稱為「URL 參數」。存取 URL 參數最有用的地方是在loadersactions 中。

export async function serverLoader({ params }) {
  return fakeDb.getAllConcertsForCity(params.city);
}

您會注意到 params 物件上的屬性名稱直接映射到您的檔案名稱:$city.tsx 變成 params.city

路由可以有多個動態區段,例如 concerts.$city.$date,兩者都可以通過名稱在 params 物件上存取

export async function serverLoader({ params }) {
  return fake.db.getConcerts({
    date: params.date,
    city: params.city,
  });
}

有關更多資訊,請參閱路由指南

巢狀路由

巢狀路由是將 URL 區段耦合到元件層級結構和資料的一般概念。您可以在路由指南中閱讀更多相關資訊。

您可以使用點分隔符建立巢狀路由。如果 . 之前的檔名與另一個路由檔名匹配,它會自動成為匹配父項的子路由。考慮以下路由

 app/
├── routes/
│   ├── _index.tsx
│   ├── about.tsx
│   ├── concerts._index.tsx
│   ├── concerts.$city.tsx
│   ├── concerts.trending.tsx
│   └── concerts.tsx
└── root.tsx

所有以 app/routes/concerts. 開頭的路由都將是 app/routes/concerts.tsx 的子路由,並在父路由的 outlet 內呈現。

網址 匹配的路由 版面配置
/ app/routes/_index.tsx app/root.tsx
/about app/routes/about.tsx app/root.tsx
/concerts app/routes/concerts._index.tsx app/routes/concerts.tsx
/concerts/trending app/routes/concerts.trending.tsx app/routes/concerts.tsx
/concerts/salt-lake-city app/routes/concerts.$city.tsx app/routes/concerts.tsx

請注意,當您新增巢狀路由時,通常需要新增索引路由,以便在使用者直接訪問父 URL 時,在父項的 outlet 內呈現某些內容。

例如,如果 URL 是 /concerts/salt-lake-city,則 UI 層級結構將如下所示

<Root>
  <Concerts>
    <City />
  </Concerts>
</Root>

沒有版面配置巢狀結構的巢狀 URL

有時您希望 URL 是巢狀的,但您不希望自動版面配置巢狀結構。您可以使用父區段上的尾隨底線選擇退出巢狀結構

 app/
├── routes/
│   ├── _index.tsx
│   ├── about.tsx
│   ├── concerts.$city.tsx
│   ├── concerts.trending.tsx
│   ├── concerts.tsx
│   └── concerts_.mine.tsx
└── root.tsx
網址 匹配的路由 版面配置
/ app/routes/_index.tsx app/root.tsx
/about app/routes/about.tsx app/root.tsx
/concerts/mine app/routes/concerts_.mine.tsx app/root.tsx
/concerts/trending app/routes/concerts.trending.tsx app/routes/concerts.tsx
/concerts/salt-lake-city app/routes/concerts.$city.tsx app/routes/concerts.tsx

請注意,/concerts/mine 不再與 app/routes/concerts.tsx 巢狀,而是與 app/root.tsx 巢狀。trailing_ 底線建立路徑區段,但它不會建立版面配置巢狀結構。

trailing_ 底線視為父項簽名末端的長位,將您排除在遺囑之外,從版面配置巢狀結構中刪除後續區段。

沒有巢狀 URL 的巢狀版面配置

我們將這些稱為無路徑路由

有時您想與一組路由共享版面配置,而無需向 URL 新增任何路徑區段。一個常見的範例是一組身份驗證路由,它們具有與公共頁面或已登入應用程式體驗不同的標頭/頁尾。您可以使用 _leading 底線來執行此操作。

 app/
├── routes/
│   ├── _auth.login.tsx
│   ├── _auth.register.tsx
│   ├── _auth.tsx
│   ├── _index.tsx
│   ├── concerts.$city.tsx
│   └── concerts.tsx
└── root.tsx
網址 匹配的路由 版面配置
/ app/routes/_index.tsx app/root.tsx
/login app/routes/_auth.login.tsx app/routes/_auth.tsx
/register app/routes/_auth.register.tsx app/routes/_auth.tsx
/concerts app/routes/concerts.tsx app/routes/concerts.tsx
/concerts/salt-lake-city app/routes/concerts.$city.tsx app/routes/concerts.tsx

_leading 底線視為您拉到檔名上的毯子,將檔名從 URL 中隱藏起來。

可選區段

將路由區段括在括號中將使該區段成為可選的。

 app/
├── routes/
│   ├── ($lang)._index.tsx
│   ├── ($lang).$productId.tsx
│   └── ($lang).categories.tsx
└── root.tsx
網址 匹配的路由
/ app/routes/($lang)._index.tsx
/categories app/routes/($lang).categories.tsx
/en/categories app/routes/($lang).categories.tsx
/fr/categories app/routes/($lang).categories.tsx
/american-flag-speedo app/routes/($lang)._index.tsx
/en/american-flag-speedo app/routes/($lang).$productId.tsx
/fr/american-flag-speedo app/routes/($lang).$productId.tsx

您可能想知道為什麼 /american-flag-speedo 正在匹配 ($lang)._index.tsx 路由,而不是 ($lang).$productId.tsx。這是因為當您有一個可選的動態參數區段,後跟另一個動態參數時,無法可靠地判斷單區段 URL(例如 /american-flag-speedo)是否應該匹配 /:lang /:productId。可選區段會積極匹配,因此它將匹配 /:lang。如果您有這種設定,建議您在 ($lang)._index.tsx loader 中查看 params.lang,並將 /:lang/american-flag-speedo 重定向到目前/預設語言(如果 params.lang 不是有效的語言代碼)。

Splat 路由

雖然動態區段匹配單一路徑區段(URL 中兩個 / 之間的部分),但 splat 路由將匹配 URL 的其餘部分,包括斜線。

 app/
├── routes/
│   ├── _index.tsx
│   ├── $.tsx
│   ├── about.tsx
│   └── files.$.tsx
└── root.tsx
網址 匹配的路由
/ app/routes/_index.tsx
/about app/routes/about.tsx
/beef/and/cheese app/routes/$.tsx
/files app/routes/files.$.tsx
/files/talks/react-conf_old.pdf app/routes/files.$.tsx
/files/talks/react-conf_final.pdf app/routes/files.$.tsx
/files/talks/react-conf-FINAL-MAY_2024.pdf app/routes/files.$.tsx

與動態路由參數類似,您可以使用 "*" 鍵在 splat 路由的 params 上存取匹配路徑的值。

export async function serverLoader({ params }) {
  const filePath = params["*"];
  return fake.getFileInfo(filePath);
}

跳脫特殊字元

如果您希望用於這些路由慣例的特殊字元實際上是 URL 的一部分,您可以使用 [] 字元跳脫慣例。這對於資源路由(包括 URL 中的擴展名)特別有用。

檔名 網址
app/routes/sitemap[.]xml.tsx /sitemap.xml
app/routes/[sitemap.xml].tsx /sitemap.xml
app/routes/weird-url.[_index].tsx /weird-url/_index
app/routes/dolla-bills-[$].tsx /dolla-bills-$
app/routes/[[so-weird]].tsx /[so-weird]
app/routes/reports.$id[.pdf].ts /reports/123.pdf

用於組織的資料夾

路由也可以是資料夾,其中包含定義路由模組的 route.tsx 檔案。資料夾中的其餘檔案不會成為路由。這允許您更靠近使用它們的路由來組織您的程式碼,而不是在其他資料夾中重複功能名稱。

資料夾內的檔案對於路由路徑沒有意義,路由路徑完全由資料夾名稱定義。

考慮以下路由

 app/
├── routes/
│   ├── _landing._index.tsx
│   ├── _landing.about.tsx
│   ├── _landing.tsx
│   ├── app._index.tsx
│   ├── app.projects.tsx
│   ├── app.tsx
│   └── app_.projects.$id.roadmap.tsx
└── root.tsx

它們中的一些或全部可以是資料夾,其中包含它們自己的 route 模組。

app/
├── routes/
│   ├── _landing._index/
│   │   ├── route.tsx
│   │   └── scroll-experience.tsx
│   ├── _landing.about/
│   │   ├── employee-profile-card.tsx
│   │   ├── get-employee-data.server.ts
│   │   ├── route.tsx
│   │   └── team-photo.jpg
│   ├── _landing/
│   │   ├── footer.tsx
│   │   ├── header.tsx
│   │   └── route.tsx
│   ├── app._index/
│   │   ├── route.tsx
│   │   └── stats.tsx
│   ├── app.projects/
│   │   ├── get-projects.server.ts
│   │   ├── project-buttons.tsx
│   │   ├── project-card.tsx
│   │   └── route.tsx
│   ├── app/
│   │   ├── footer.tsx
│   │   ├── primary-nav.tsx
│   │   └── route.tsx
│   ├── app_.projects.$id.roadmap/
│   │   ├── chart.tsx
│   │   ├── route.tsx
│   │   └── update-timeline.server.ts
│   └── contact-us.tsx
└── root.tsx

請注意,當您將路由模組變成資料夾時,路由模組會變成 folder/route.tsx,資料夾中的所有其他模組都不會變成路由。例如

# these are the same route:
app/routes/app.tsx
app/routes/app/route.tsx

# as are these
app/routes/app._index.tsx
app/routes/app._index/route.tsx
文件和範例 CC 4.0