Remixのフォルダ構成
features構成からの発展形
ReactでSPAを作るときは bulletproof-react をベースにしたfeatures構成が多いと思う。自分もSPAの場合はコレを模したフォルダ構成にしている。
features構成の特徴はファイルごとにフォルダを分割する所謂Railsスタイルとは違う。関心事(ドメイン)別にフォルダを分割する。
Remixでもroutes以外の構成はfeatures構成にするのが良きだと脳死で思っていた。が、しかし、Remixのドキュメントを読むとどうやら他にも良き案があるらしい。features構成からさらに発展したようなイメージだ。
Remixの特殊なフォルダ機能
ドキュメントの Folders for Organization にこう記載がある。
ルートはフォルダにすることもでき、その中にroute.tsxファイルを置いてルートモジュールを定義することができます。フォルダ内の他のファイルはルートにはなりません。これにより、コードをそれを使用するルートにより近い場所で整理することができ、他のフォルダ間でフィーチャー名を繰り返す必要がなくなります。
フォルダ内のファイルはルートパスに対して何の意味も持ちません。ルートパスは完全にフォルダ名によって定義されます。
app/routes
以下にフォルダがある場合は route.tsx
のみがページに関わるファイルになり、それ以外のファイルはページファイルとならないのだ。
routes/
├ user/
└ route.tsx
└ profile.tsx
こういうフォルダ構成の場合URLは、
/user
/user/profile
を期待してしまうと思うが、/user/profile
というURLは存在しない。フォルダ内の route.tsx
のみがページコンポーネントと成り得る。user/profile
としたい場合は以下のようになる。
routes/
├ user/
└ route.tsx
├ user.profile/
└ route.tsx
Remixでは .
(ドット) がURLのスラッシュになるのだ。これを発展させると以下のようになる。
routes/
├ user/
└ components
└ hooks
├ user._index/
└ route.tsx
└ components
├ user.profile/
└ route.tsx
└ components
URL user/
以下で利用するファイルはすべて user
フォルダにまとめた。
ページごとに利用するファイルはそのページのフォルダ以下に集約する。
_
接頭辞がついたディレクトリは無視されるので、user._index/route.tsx
は URL /user
になる。
古き良きウェブサイトのフォルダ構成に戻ったようだ。
全体的で共通で利用するファイル群は正直どこでもいい。routesと同階層とか。この辺はあまり深く考えない。features構成を取り入れてもいいと思うけど自分はなるべくシンプルにしておきたいので以下のようにしている。
app/
├ components/
├ hooks/
├ routes/
フォルダ構成の細部はプロジェクトを進むにつれて、そのプロジェクトにマッチするように構成を作っていけばよいと思う。最初から複雑な構成を作るのはどうにも好きではない。
そして_
接頭辞機能を利用して、さらに素敵なフォルダ構成ができる。
_接頭辞付き共通フォルダ
_
接頭辞をつけるとそのフォルダはURL上では無視される、と書いた。それを利用して以下のようにフォルダ設計をする。
├── routes
│ ├── _auth
│ ├── _auth.register
│ ├── _auth.login
│ ├── _auth.reset-password
このようなURLになる。
/register
/login
/reset-password
これら3つのURLの共通レイアウトを _auth/route.tsx
に定義することができる。
import { Outlet } from "@remix-run/react";
import { SiteFooter, SiteHeader } from "~/components";
export default function AuthCommon() {
return (
<div className="grid grid-rows-[auto_1fr_auto] h-dvh">
<SiteHeader />
<Outlet />
<SiteFooter />
</div>
);
}
上記3つのpageコンポーネント(例えば/routes/_auth.register/route.tsx
)は_auth/route.tsx
の<Outlet />
の部分に表示される。
これはドキュメントの Nested Layouts without Nested URLs の項で発見した。
パスレスルート(Pathless Routes)と呼ぶらしい。
時には、URLにパスセグメントを追加せずに、一群のルートでレイアウトを共有したいことがあります。よくある例として、公開ページやログイン後のアプリ体験とは異なるヘッダー/フッターを持つ認証関連のルートセットがあります。これは_(先頭のアンダースコア)を使って実現できます。
とても素敵な機能だ。
当初はfeatures構成を取り入れて、routesフォルダにはページコンポーネントしか置かない手法がいいのかと思っていた。自分が経験したNext.jsのプロジェクトはそのような構成が多かった。
Remixはさらにそれを発展させてroutesフォルダ以下に大半のコードを置き、関心事同士をより近距離にすることができる。
Remix楽しい。
Discussion