ページとルーティング
BobはMegaShopに商品詳細ページを追加する際, 50のvue-routerルートを手動設定しました。新商品が出品されるたびにルートファイルを編集する必要があり, 非常に面倒でした。CharlieはNuxt 3がファイル規約に基づいてルートを自動生成することを教えました。ファイルを作成するだけでルートが作成されます。
1. 学ぶ内容
- ファイルルーティング:
pages/ディレクトリのルーティング完全ルール - 動的ルーティング:[id].vueと[...slug].vueのパラメータキャプチャ
- ネストルートとレイアウト:親ページ+ネストされた子スロット
- プログラマティックナビゲーション:navigateTo() / useRouter() / useRoute()
- MegaShop多段動的ルーティングのハンズオンガイド
2. 管理者の実話
(1) ペインポイント:手動ルーティング設定の悪夢
Bobは従来のVue Routerを使ってMegaShopに1,000のルートを設定しました。各カテゴリと各商品ページに1つずつです。新商品が出品されるたびにルートファイルを編集する必要があり, 1つでも忘れると404エラーになりました。Aliceは商品を見つけられず, クレーム率が急増しました。
(2) Nuxtファイルルーティングによる解決策
Nuxt 3のファイルベースルーティングでは, Bobはファイルを作成するだけです:
TEXT
pages/products/[id].vue → /products/:id
100万商品が1つのファイルで済み, 動的パラメータは自動的に渡されます。
(3) 効果:ゼロ設定ルーティング
Bobはもうルート設定を書く必要がありません。新しいページの追加は新しいファイルを作成するだけで済み, 100万商品の詳細ページもたった1つの[id].vueで対応できます。
3. ファイルルーティングルール
(1) ディレクトリからルートへのマッピングプロセス
flowchart LR
A[pages/index.vue] -->|"/"| B[ルートルート]
C[pages/about.vue] -->|"/about"| C2[Aboutルート]
D[pages/products/index.vue] -->|"/products"| D2[商品一覧ルート]
E[pages/products/[id].vue] -->|"/products/:id"| E2[商品詳細ルート]
F[pages/categories/[...slug].vue] -->|"/categories/:slug*"| F2[キャッチオールルート]
(2) ファイルルーティングマッピングルール
| ファイルパス | 生成されるルート | 説明 |
|---|---|---|
| pages/index.vue | / | ホーム |
| pages/about.vue | /about | 静的ページ |
| pages/products/index.vue | /products | 商品一覧 |
| pages/products/[id].vue | /products/:id | 動的パラメータ |
| pages/categories/[slug].vue | /categories/:slug | 動的パラメータ |
| pages/categories/[...slug].vue | /categories/:slug(*) | ワイルドカードルート |
| pages/404.vue | /404 | カスタム404 |
(1) ▶ サンプル:基本ページルーティング
VUE
<!-- pages/index.vue -->
<template>
<div>
<h1>MegaShop - 数百万の商品</h1>
<NuxtLink to="/products">商品を閲覧</NuxtLink>
</div>
</template>
出力:
TEXT
// 実行成功
VUE
<!-- pages/about.vue -->
<template>
<div>
<h1>MegaShopについて</h1>
<p>世界中に100万以上の商品をお届け</p>
</div>
</template>
4. 動的ルーティング
(1) 単一パラメータの動的ルーティング
[param]構文を使って動的ルーティングパラメータを定義します。
(1) ▶ サンプル:商品詳細ページの動的ルーティング
VUE
<!-- pages/products/[id].vue -->
<template>
<div>
<h1>商品 #{{ route.params.id }}</h1>
<p>価格: ${{ product?.price }} USD</p>
</div>
</template>
<script setup lang="ts">
const route = useRoute()
const { data: product } = await useFetch(`/api/products/${route.params.id}`)
</script>
出力:
TEXT
// 実行成功
(2) ワイルドカードルート
[...slug]構文を使って多段パスをキャプチャします。
(2) ▶ サンプル:多段カテゴリワイルドカードルーティング
VUE
<!-- pages/categories/[...slug].vue -->
<template>
<div>
<h1>カテゴリ: {{ slugPath }}</h1>
<p>{{ products.length }} 件の商品が見つかりました</p>
</div>
</template>
<script setup lang="ts">
const route = useRoute()
// /categories/electronics/headphones → slug = ['electronics', 'headphones']
const slugPath = computed(() => (route.params.slug as string[]).join('/'))
const { data: products } = await useFetch(`/api/categories/${slugPath.value}`)
</script>
出力:
TEXT
// 実行成功
(3) 動的ルーティングパラメータの型
| 構文 | マッチ | URLの例 | paramsの値 |
|---|---|---|---|
| [id] | 単一セグメント | /products/123 | { id: '123' } |
| [id].vue + index.vue | オプション | /products または /products/123 | { id?: '123' } |
| [...slug] | 複数セグメントワイルドカード | /a/b/c | { slug: ['a','b','c'] } |
5. ネストルート
(1) ネストルーティング構造
ネストルーティングには親ページ (同名の.vueファイル)と子ディレクトリの組み合わせが必要です:
TEXT
pages/
├── products/
│ ├── index.vue # /products (子)
│ ├── [id].vue # /products/:id (子)
│ └── edit.vue # /products/edit (子)
└── products.vue # /products (親, <NuxtPage />を持つ)
(1) ▶ サンプル:商品ページのネストルート
VUE
<!-- pages/products.vue - 親レイアウト -->
<template>
<div>
<nav class="product-nav">
<NuxtLink to="/products">全商品</NuxtLink>
<NuxtLink to="/products/featured">注目商品</NuxtLink>
</nav>
<!-- 子ルートはここにレンダリング -->
<NuxtPage />
</div>
</template>
出力:
TEXT
// 実行成功
VUE
<!-- pages/products/index.vue - 子:商品一覧 -->
<template>
<div>
<h1>全商品</h1>
<p>100万商品がご利用いただけます</p>
</div>
</template>
VUE
<!-- pages/products/[id].vue - 子:商品詳細 -->
<template>
<div>
<h1>商品詳細</h1>
<p>ID: {{ route.params.id }}</p>
</div>
</template>
(2) ネストルートとレイアウトの比較
| 項目 | ネストルーティング | レイアウト |
|---|---|---|
| 仕組み | 親ページ + NuxtPageスロット | layouts/ + NuxtLayout |
| ルーティング関係 | 親子ルートがレイアウトを共有 | ページレベルのレイアウト切替 |
| データ取得 | 親と子がそれぞれuseFetchを使用 |
レイアウトはデータを取得しない |
| ユースケース | カテゴリ → 商品 → レビューの階層 | 全体的なページレイアウト (ヘッダー/フッター) |
6. プログラマティックナビゲーション
(1) 3つのナビゲーション方法の比較
| API | 目的 | サーバー側で利用可能 | ユースケース |
|---|---|---|---|
| navigateTo() | プログラマティックナビゲーション | ✅ | ログイン後のリダイレクト |
| useRouter().push() | クライアント側ナビゲーション | ❌ | ボタンクリックでのナビゲーション |
| NuxtLink | 宣言的リンク | ✅ | テンプレート内のナビゲーション |
(1) ▶ サンプル:navigateToサーバー側リダイレクト
TYPESCRIPT
// ミドルウェアまたはサーバー側コンテキスト内
export default defineNuxtRouteMiddleware((to) => {
const isAuthenticated = useState('isAuthenticated')
if (!isAuthenticated.value && to.path.startsWith('/admin')) {
return navigateTo('/login', { redirectCode: 302 })
}
})
出力:
TEXT
// 実行成功
(2) ▶ サンプル:useRouterクライアント側ナビゲーション
VUE
<template>
<button @click="goToProduct(product.id)">
詳細を見る
</button>
</template>
<script setup lang="ts">
const router = useRouter()
function goToProduct(id: number) {
router.push(`/products/${id}`)
}
</script>
出力:
TEXT
// 実行成功
(3) ▶ サンプル:useRouteでパラメータを取得
VUE
<script setup lang="ts">
const route = useRoute()
// パラメータにアクセス
const productId = route.params.id // [id].vueから
const categorySlug = route.params.slug // [...slug].vueから
// クエリパラメータにアクセス
// /products?category=electronics&sort=price
const category = route.query.category // 'electronics'
const sort = route.query.sort // 'price'
</script>
出力:
TEXT
// 実行成功
7. MegaShop多段ルーティングのハンズオンガイド
(1) MegaShopルーティングプラン
| URL | ファイル | 機能 |
|---|---|---|
| / | pages/index.vue | ホーム |
| /products | pages/products/index.vue | 商品一覧 |
| /products/[id] | pages/products/[id].vue | 商品詳細 |
| /categories/[category] | pages/categories/[category].vue | カテゴリページ |
| /categories/[category]/[subcategory] | pages/categories/[...slug].vue | サブカテゴリ |
| /cart | pages/cart.vue | ショッピングカート |
| /checkout | pages/checkout.vue | チェックアウト |
| /admin | pages/admin/index.vue | 管理ダッシュボード |
| /admin/products | pages/admin/products/index.vue | 商品管理 |
| /404 | pages/404.vue | 404ページ |
(1) ▶ サンプル:MegaShop 404ページ
VUE
<!-- pages/404.vue -->
<template>
<div class="not-found">
<h1>404 - 商品が見つかりません</h1>
<p>お探しの商品は存在しません。</p>
<NuxtLink to="/products">全商品を閲覧</NuxtLink>
</div>
</template>
出力:
TEXT
// 実行成功
8. 総合例:MegaShopルーティングシステム
VUE
<!-- pages/index.vue - ホームページ -->
<template>
<div>
<section class="hero">
<h1>MegaShop</h1>
<p>100万以上の商品を世界中にお届け</p>
<NuxtLink to="/products">今すぐショッピング</NuxtLink>
</section>
<section class="featured">
<ProductCard v-for="p in featured" :key="p.id" :product="p" />
</section>
</div>
</template>
<script setup lang="ts">
const { data: featured } = await useFetch('/api/products/featured')
</script>
VUE
<!-- pages/products/[id].vue - 商品詳細 -->
<template>
<div v-if="product">
<h1>{{ product.name }}</h1>
<p class="price">${{ product.price }} USD</p>
<button @click="addToCart">カートに追加</button>
<button @click="goBack">一覧に戻る</button>
</div>
<div v-else>
<p>商品が見つかりません</p>
<NuxtLink to="/products">商品を閲覧</NuxtLink>
</div>
</template>
<script setup lang="ts">
const route = useRoute()
const router = useRouter()
const { data: product } = await useFetch(`/api/products/${route.params.id}`)
function addToCart() {
navigateTo('/cart')
}
function goBack() {
router.back()
}
</script>
❓ よくある質問
Q 動的ルーティングのパラメータは文字列ですか, 数値ですか?
A URLパラメータは常に文字列です。数値が必要な場合は手動で変換してください:
Number(route.params.id)。またはuseFetch使用時にAPIの戻り値の型で決定します。Q [id].vueと[...slug].vueの違いは何ですか?
A [id]は単一のパスセグメントのみマッチします (/products/123)。[...slug]は複数セグメントのパスをマッチします (/a/b/c)。
slugは配列になります。キャッチオールルートは階層が無制限のカテゴリに適しています。Q ネストルートの親ページは必須ですか?
A はい。親ページ
products.vueとサブディレクトリproducts/の両方が存在して初めてネストルートが形成されます。子ルートのみが必要な場合は, 親ページを作成しないでください。Q NuxtLinkと通常の
aタグの違いは何ですか?A NuxtLinkはクライアント側ナビゲーションに使用されます (ページをリフレッシュしない)。
aタグはフルページリフレッシュを引き起こします。内部ナビゲーションにはNuxtLinkを, 外部リンクにはaタグを使用してください。Q カスタム404ページの設定方法は?
A
pages/404.vueファイルを作成するだけです。Nuxt 3はルートがマッチしない場合に自動的にこのページを表示します。キャッチオールルートで処理することも可能です。Q
routeRulesとルーティングの関係は何ですか?A
routeRulesはnuxt.config.tsで設定し, ルートパスに基づいてレンダリング戦略 (SSR/ISR/CSR)を指定します。ルートの定義には影響しません。ルートはpages/ディレクトリのファイルで定義され, routeRulesはレンダリング動作のみを制御します。📖 まとめ
- Nuxt 3ファイルルーティング:
pages/の下のファイルが自動的にルートにマッピングされ, ゼロ設定 - 動的ルーティングは[id].vueで単一パラメータを, [...slug].vueで多段パスをキャプチャ
- ネストルートには同名の親ページ+NuxtPageスロットが必要で, レイアウトとは異なる概念
- プログラマティックナビゲーション:サーバー側では
navigateTo(), クライアント側ではuseRouter().push() - MegaShop:100万商品がたった1つの[id].vueファイルで対応可能
📝 練習問題
- 基本問題 (難易度:⭐):pages/about.vueとpages/contact.vueを作成し, ルートが自動生成されることを検証してください。
- 応用問題 (難易度:⭐⭐):
/products/[category]/[id]の2段動的ルーティングを実装し, ページ上に「カテゴリ」と「id」パラメータを表示してください。 - チャレンジ (難易度:⭐⭐⭐):キャッチオールルート[...slug].vueを実装し,
/docs/getting-started/installationのような多段パスをレンダリングし, パンくずナビゲーションバーを表示してください。
---|



