ルーティング
pages/ 配下のファイルシステムルーティングが zfb で URL にどうマッピングされるか。
zfb は pages/ ディレクトリ配下で ファイルシステムルーティング を採用しています。ルーターはビルド時(および dev では変更のたびに)pages/ をスキャンし、各ページのソースファイルをルートへと変換します。この規約は Next.js や Astro で見覚えのあるものと一致します。
ファイルからルートへのマッピング
| File | Route | Notes |
|---|---|---|
pages/ | / | |
pages/ | / | |
pages/ | / | SSG 専用。MDX パイプライン |
pages/ | / | SSG 専用。静的アセットのコピー |
pages/ | / | |
pages/blog/[slug].tsx | /(動的) | |
pages/docs/[...slug].tsx | /(catchall) | |
pages/docs/[[...slug]].tsx | /(オプショナル catchall) | ベース URL の / にもマッチ |
pages/[lang]/[slug].tsx | / |
最初に押さえておきたいルールがいくつかあります。
_で始まるファイル(例:_app.tsx)は無視されます。ルートの隣に置きたいが公開はしたくない共有ヘルパーには、このプレフィックスを使ってください。ページとして受け付けられる拡張子は
.tsx・.mdx・.md・.htmlです。pages/内のそれ以外の拡張子のファイルはスキップされます(警告がログに出ます)。そのため README やメモを置いても問題ありません。同じルートに解決される 2 つのファイルがあると、ビルド時に
RouterError::AmbiguousRouteが発生します。ルーターが暗黙のうちに勝者を選ぶことはありません。ルーターはまた、2 つのルートがパラメータ名だけ異なり同じ URL にマッチする場合(例:docs/[a].tsxとdocs/[b].tsx)にRouterError::AmbiguousShapeを、オプショナル catchall が同じ位置で別のルートと重なる場合にRouterError::OptionalCatchallConflictを発生させます。
.md と .html のページエントリの完全なコントラクトと v1 の制限については Markdown and HTML Pages を参照してください。
スキャンは zfb-router クレートの Router::scan が行います。結果は 静的ルートが動的ルートより優先され、動的ルートが catchall より優先される ようにソートされます。より具体的なルートが先にマッチします。
静的・動的・catchall ルート
静的ルート(pages/)は単一の具体的な URL にマッチします。動的ルートはファイル名に [param] の角括弧を使って単一のパスセグメントをキャプチャし、catchall ルートは [...param] を使って末尾の任意個数のセグメントをキャプチャします。
catchall は オプショナル にもできます。二重角括弧の [[...param]] はディレクトリ直下のベース URL にもマッチします — pages/docs/[[...slug]].tsx は / に加えて /(slug = [])も配信します。必須形の [...param] は厳密なままで、ゼロセグメントには決してマッチしません。オプショナル catchall はルートの最後のセグメントでなければならず、同じ位置の兄弟 index.tsx(または同位置の [...param])とは共存できません — 同じ URL を取り合うため、ルーターはスキャン時にこの組み合わせを拒否します。
// pages/blog/[slug].tsx
export default function BlogPost({ slug }: { slug: string }) {
return <article>Post for {slug}</article>;
}// pages/docs/[...slug].tsx
export default function DocsPage({ slug }: { slug: string[] }) {
return <main>{slug.join("/")}</main>;
}paths() エクスポート
動的ルートと catchall ルートは、ビルド時にどの具体的な URL をレンダリングするかを知る必要があります。これは同じファイルから paths() 関数をエクスポートすることで実現します。
// pages/blog/[slug].tsx
export function paths() {
const posts = getCollection("blog");
return posts.map((p) => ({ params: { slug: p.slug } }));
}
export default function BlogPost({ params }: { params: { slug: string } }) {
return <article>Post {params.slug}</article>;
}paths() はビルド中に組み込みの V8 ホストによって評価されます。zfb build は静的・動的・catchall ルートを検出し、各動的・catchall ルートについて paths() を評価して、レンダリングすべき具体的な URL を列挙します。静的ルートに paths() エクスポートは不要です。paths() はルートごと・ビルドごとに 1 回だけ評価されます — 内部でのエントリごとの処理は安全で、出力 URL ごとに繰り返されることはありません。