3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Remixの次のメジャーバージョンにて導入される新しいルートファイルの命名規則について

Last updated at Posted at 2023-03-17

はじめに

Remixの次期メジャーバージョンでは、新しいルートファイルの命名規則が導入される予定です。
実はこの機能、remix.config.jsfutureブロックにて、v2_routeConventiontrueに設定することで一足先に試すことができるようになっています。

remix.config.js
module.exports = {
  future: {
    v2_routeConvention: true,
  },
};

この機能は、Remixのバージョン1.11.0から利用可能となっているため、それ以前のバージョンをご使用の場合はご注意ください。

V1からV2に移行すべき人

V2の主な目的は、フラットルートファイルシステム規約のサポートです。

簡単にいうと、今までパスを区切るためにフォルダをネストさせていました。
このため、特定のファイルを開くために何層ものフォルダを潜らないといけません。

app/
├── routes/
│   ├── _index.tsx
│   ├── about.tsx
│   ├── concerts/
│   │   ├── trending.tsx
│   │   ├── salt-lake-city.tsx
│   │   └── san-diego.tsx
└── root.tsx

フラットルートファイルシステム規約がサポートされると、パスを表現するためにフォルダをネストさせる必要がなくなり、1つの階層にすべてのファイルを置くことができるようになるため、生産性が大きく向上されことが期待されています。

app/
├── routes/
│   ├── _index.tsx
│   ├── about.tsx
│   ├── concerts.trending.tsx
│   ├── concerts.salt-lake-city.tsx
│   └── concerts.san-diego.tsx
└── root.tsx

ただ、今まで通りフォルダをネストすることでURLのパスを表現する方が好きだという人は無理に移行する必要はありません。従来通り、何も気にせず開発を続けることが公式に推奨されています。

ドット区切り

ファイル名を.で区切ることで、URL上でパスを区切ることができます。

app/
├── routes/
│ ├── \_index.tsx
│ ├── about.tsx
│ ├── concerts.trending.tsx
│ ├── concerts.salt-lake-city.tsx
│ └── concerts.san-diego.tsx
└── root.tsx

例えば、次のファイル名は、次のURLに一致します。

URL Matched Route
/concerts/trending concerts.trending.tsx
/concerts/salt-lake-city concerts.salt-lake-city.tsx
/concerts/san-diego concerts.san-diego.tsx

ネストされたルート

ドット区切りのファイルは、ネストされたルートを表すことができます。この時の親子関係は次のようになります。

例えば、次のようなファイル構成になっているとします。

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

それぞれのルートを<Outlet />を通してレンダリングする親に相当するレイアウトファイルとの関係は次のようになります。
つまり、concerts.xxx.tsxのようにconcertsで始まっていて、routesフォルダの中にconcerts.tsxが存在するのであれば、このconcerts.tsxがレイアウトファイルとして利用されます。

URL Matched Route Layout
/ _index.tsx root.tsx
/about about.tsx root.tsx
/concerts concerts._index.tsx concerts.tsx
/concerts/trending concerts.trending.tsx concerts.tsx
/concerts/salt-lake-city concerts.$city.tsx concerts.tsx

しかし、URL上は変更したくないけれども、レイアウトファイルとしてconcerts.tsxではなく、root.tsxに親になってもらいたい場合があります。
その場合は、concerts_を付与して、concerts\_.xxx.tsxというファイル名を使用します。

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

この場合はの対応は次のようになります。

URL Matched Route Layout
/ _index.tsx root.tsx
/concerts/mine concerts_.mine.tsx root.tsx
/concerts/trending concerts.trending.tsx concerts.tsx
/concerts/salt-lake-city concerts.$city.tsx concerts.tsx

URLのパスには親レイアウトファイルの名前は入れたくないけども、グルーピングとして共通の親レイアウトファイルを持ち場合があります。これをパスレスルートと呼んでいますが、これを実現するためには、そのセグメント名の先頭に\_を付与します。

app/
├── routes/
│ ├── \_auth.login.tsx
│ ├── \_auth.register.tsx
│ ├── \_auth.tsx
│ ├── \_index.tsx
│ ├── concerts.$city.tsx
│ └── concerts.tsx
└── root.tsx

この場合のURLとファイルの対応は次のようになります。

URL Matched Route Layout
/ _index.tsx root.tsx
/login _auth.login.tsx _auth.tsx
/register _auth.register.tsx _auth.tsx
/concerts/salt-lake-city concerts.$city.tsx concerts.tsx

これにより、URLからauthの文字を隠すことができるようになっています。

ダイナミックセグメント

動的にURLの特定のセグメントをマッチさせることで、その値をparamsから受け取るようにすることができます。特定のセグメントを動的に決定させるためには、$を使用します。

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

例えば、次のファイル名は、次のURLに一致します。
この場合、$citysalt-lake-cityが入るため、loader()action()にてparams.cityを使うことでその値にアクセスすることができます。

URL Matched Route
/concerts/trending concerts.trending.tsx
/concerts/salt-lake-city concerts.$city.tsx
/concerts/san-diego concerts.$city.tsx

ちなみに、concerts.$city.$date.tsxというファイル名を設定することも可能となっています。
この場合、params.cityparams.dateにてそれぞれのセグメントの値にアクセスすることができます。

オプショナルセグメント

\$が付いたルートセグメントをカッコで囲むことで、そのセグメントは任意の扱いとなります。

app/
├── routes/
│ ├── ($lang)._index.tsx
│   ├── ($lang).$productId.tsx
│   └── ($lang).categories.tsx
└── root.tsx

これにより、/categoriesでも/en/categoriesでもアクセスできるようになります。
主な対応は次のとおりです。

URL Matched Route
/ ($lang)._index.tsx
/categories ($lang).categories.tsx
/en/categories ($lang).categories.tsx
/fr/categories ($lang).categories.tsx
/american-flag-speedo ($lang).$productId.tsx
/en/american-flag-speedo ($lang).$productId.tsx
/fr/american-flag-speedo ($lang).$productId.tsx

スプラットセグメント

スプラットセグメントを行うために、ファイル名の拡張子の前に必ず$を使います。
これにより、URLの残りの部分とマッチさせることができるようになります。

app/
├── routes/
│ ├── \_index.tsx
│ ├── $.tsx
│   ├── about.tsx
│   └── files.$.tsx
└── root.tsx

例えば、files.$.tsxは、/files/a/b/c/d/e/f/gにもマッチします。

URL Matched Route
/ _index.tsx
/beef/and/cheese $.tsx
/files/ files.$.tsx
/files/talks/remix-conf_old.pdf files.$.tsx
/files/talks/remix-conf_final.pdf files.$.tsx
/files/talks/remix-conf-FINAL-MAY_2022.pdf files.$.tsx

paramsで値にアクセスするためには、params["*"]を使います。

// app/routes/files.$.tsx
export function loader({ params }) {
  let filePath = params["*"];
  return fake.getFileInfo(filePath);
}

特殊文字のエスケープ

URLの一部として、$[]などの特殊文字を使用したい場合は、[]で値を囲むことでエスケープすることができるようになっています。

Filename URL
routes/sitemap[.]xml.tsx /sitemap.xml
routes/[sitemap.xml].tsx /sitemap.xml
routes/weird-url.[_index].tsx /weird-url._index
routes/dolla-bills-[$].tsx /dolla-bills-$
routes/[[so-weird]].tsx /[so-weird]

# フォルダを使ったルート定義

今までは、ファイル名でURL上のセグメントを定義して、ファイルの中身としてルートモジュールを定義していました。
実はフォルダ名として、ルートパスを定義し、そのフォルダ内のroute.tsxでルートモジュールを定義するという方法があります。

例えば、今までは次のようにドット区切りを使ってルートパスを定義していました。そしてそれぞれのファイルがルートモジュール本体を表していました。

routes/
  _landing._index.tsx
  _landing.about.tsx
  _landing.tsx
  app._index.tsx
  app.projects.tsx
  app.tsx
  app_.projects.$id.roadmap.tsx

このルートパスの定義をフォルダ側に移して、その中にroute.tsxというファイルを作成します。そして、そのroute.tsx内でルートモジュールを定義します。
このとき、そのフォルダ内にあるroute.tsx以外のファイルは、ルートパスとしての役割を一切果たしません。そのため、そのフォルダ内のroute.tsxでしか使用されない、そのパスでの機能に特化した関数やコンポーネントのファイルを配置することが可能となり、整理することができるようになっています。

その一部、あるいは全部が、内部に独自のルートモジュールを保持するフォルダーになることもあります。

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

例えば、次のフォルダとファイルの関係は同じものを意味しています。

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

# as are these
routes/app._index.tsx
routes/app._index/route.tsx

この方法は、Remixで最も推奨されている方法であり、そのルートでしか使用されないモジュールとすべてのルートで共通のモジュールを簡単に認識できるようになるため、ファイル構造が散らかるのを防ぎ、リファクタリングの容易性を格段に向上させることが期待されています。

まとめ

基本的な考え方は、V1がベースとなっているため、V1の命名規則に慣れており、ある程度不満を抱えていた開発者にとっては待望の機能となっています。
また、V1のままでもファイル整理疲れを起こさずに管理できるという開発者にとっては、あまりメリットが感じられないと思うので、無理に移行させることはお勧めしません。

参考にした記事

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?