Remixでルートを定義する
本連載の中でも時折解説してきましたが、Remixは app/routes/
フォルダの中に配置した、ファイルやフォルダの名前でURLのパスを決定する仕組みを備えています。この仕組みは、公式ドキュメントの中では "File System Route Convention" という言葉で言及されています。省略されて、単に "file convention" と呼ばれることもありますが、大意としては「ファイルシステムを活用したルート定義規約」といったところです。
それでは、ルート定義のルールを一つずつ確認していきましょう。
HTMLのベースを定義する
さて、app/routes/
の扱い方の話を始める前段として、Web標準の世界とRemixのルーティングがどう繋がるのかをお話ししておきましょう。といっても複雑な話ではありません。ブラウザに表示するページというのは <html>
要素を最上位に持つDOMツリーを持たないといけませんよね、という至極当たり前の話です。
Remixで <html>
要素を定義しているのは、app/root.jsx
です。このファイルには、Remixを用いて表示するすべてのページをHTML文書として成立させるためのベースとしての役割があります。
では、実際のファイルの中を見てみましょう(リスト1)。
import { cssBundleHref } from "@remix-run/css-bundle"; import { Links, LiveReload, Meta, Outlet, Scripts, ScrollRestoration, } from "@remix-run/react"; export const links = () => [ ...(cssBundleHref ? [{ rel: "stylesheet", href: cssBundleHref }] : []), ]; export default function App() { return ( <html lang="en"> <head> <meta charSet="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <Meta />{/ (2) /} <Links />{/ (1) /} </head> <body> <Outlet />{/ (6) /} <ScrollRestoration />{/ (5) /} <Scripts />{/ (3) /} <LiveReload />{/ (4) /} </body> </html> ); }
眺めてみると、<html>
や <head>
や <body>
といった見慣れた要素が記載されています。これらの要素は、基本的にそのままDOMツリーへと描画されます。また、サーバーサイドレンダリングでHTMLファイルを生成するタイミングがあれば、そのベースとしても使われます。
(1)の <Links />
、(2)の <Meta />
、(3)の <Scripts />
は、各種設定情報を配置するためのプレースホルダーです。このrootのコンポーネントを元に各ページが描画されるタイミングで、そのページにとって適切な内容の <link>
や <meta>
や <script>
といった具体的なDOM要素に置き換わります。
(4)の <LiveReload />
は開発用の便利ツールです。npm run dev
で開発サーバーを立ち上げている間、ソースコードの変更を検知して、ブラウザに表示中のアプリケーションを自動で更新するための手助けをしてくれます。
(5)の <ScrollRestoration />
は便利ライブラリで、各ページのスクロール位置を保存して、画面を進んだり戻ったりしても、元のスクロール位置で表示してくれます。これが標準で搭載されているのは、Remixの密かな良いところです。
最後に、(6)の <Outlet />
は、ルーティングを表現するためのプレースホルダーです。app/routes/
に配置したルートファイルの内容は、このコンポーネントの場所に描画されます。初期表示が終わって、シングルページアプリケーションとして画面遷移を始めると、<Outlet />
の中身だけが切り替わっていくイメージです。本記事の大半の話題は、結果的に <Outlet />
の中身をどう切り替えていくか、という話題に終着します。
ちなみに、app/root.jsx
は後述するNested Routes機能を用いて作られたレイアウトの一種であり、その機能の一環として <Outlet />
を使うことができています。ここでは詳しく説明しませんが、後ほどまた言及します。
[コラム]ルートとルート
Remixの解説には、app/root.jsx
のルート(root)と app/routes/
内に配置するルート(route)という2つの「ルート」が登場します。日本語でカタカナ表記する際には、どちらも同じ「ルート」と記載し、発音にも特に違いを設けないので、少し紛らわしいですね。
app/root.jsx
の "root" はDOMツリーやURLのパスの大元である「根」を表しており、app/routes/
内に配置するコンポーネントを表す "route" はWebサイト内の「経路」を表しています。解説の中で言及する「ルート」は大半が "route" のことになりますが、本記事のようにどちらも扱う場合には、可能な限り "root" と "route" のどちらなのかを併記し、わかりやすくしておきます。
基本のルート
それでは、ルート(route)を作成するための基本から解説していきましょう。Remixでは、app/routes/
フォルダの直下にReactコンポーネントのファイルを置くと、ルートとして認識されます。このファイルは、何らかのReactコンポーネントを export default
で外部に公開している必要があります。
例えば app/routes/top20.jsx
のようなファイルを置いたときはどうなるでしょうか(図1)。
http://localhost:3000/top20
というURLでアクセスできていますね。まずは、これがすべての基本です。
さて、ファイル名がURLのパスになるとなると、困ったことが出てきます。 http://localhost:3000/
のようなルートパス(root path)を表現した場合にはどうすればよいのでしょうか。まさか空文字に拡張子を付けたファイル名を作るわけにもいきません。
実は、ルートパスを表現するための特殊なファイル名として、_index.jsx
という名前が用意されています(図2)。
app/route/_index.jsx
に定義されたコンポーネントが、http://localhost:3000/
で表示できましたね。
ルートに付随する情報
前回までの解説で、ルート(route)として定義したファイルに loader
や action
という名前の関数を配置してエクスポートしておくことで、通信機能を利用できることがわかっています。実は、それ以外にも、図2で写り込んでいる meta
関数のように、ルートのファイル内に配置することで特別な役割を担う関数やコンポーネントがあります。今回は詳しく解説しませんが、概要は表1の通りです。
名称 | 種別 | 概要 |
---|---|---|
action | 関数 | フォームからのsubmitリクエストを受け付ける関数 |
ErrorBoundary | コンポーネント | コンポーネント内で起きたエラーをハンドリングしてエラー表示を出すためのコンポーネント |
handle | オブジェクト | パンくずリストに載せたい自ページの情報を登録する |
headers | 関数 | HTTPヘッダーを定義してCache-Controlなどを制御する |
links | 関数 | <link> 要素を定義してCSSなどを制御する |
loader | 関数 | データフェッチのための関数 |
meta | 関数 | <title> 要素や <meta> 要素を定義する |
shouldRevalidate | 関数 | どのようなイベントが発生したときにloaderを再実行するのかを定義する |
これまでの例の中で見かけたものもありますね。links
関数は リスト1で app/root.jsx
に記載されていましたし、図2には meta
関数が写り込んでいました。
Remixはページを表示するタイミングで、これらの関数やオブジェクトに定義された情報を集計し、app/root.jsx
に記載されていた <Links />
や <Meta />
といったコンポーネントの内部実装として <link>
要素や <meta>
要素を埋め込んでいくことになります。