T-CREATOR

Nuxt のページ遷移を極める:ファイルベースルーティング攻略法

Nuxt のページ遷移を極める:ファイルベースルーティング攻略法

Nuxt.js の最大の魅力の一つが、ファイルベースルーティングです。従来の Vue Router のように手動でルートを定義する必要がなく、ファイルシステムの構造がそのまま URL 構造になるという革命的な仕組みです。

この記事では、Nuxt のページ遷移を完全に理解し、実践的なプロジェクトで活用できるようになることを目指します。初心者の方でも安心して読み進められるよう、基礎から応用まで段階的に解説していきます。

ファイルベースルーティングをマスターすれば、開発効率が劇的に向上し、保守性の高いアプリケーションを構築できるようになります。一緒に Nuxt の世界を深く探求していきましょう。

ファイルベースルーティングの基本概念

Nuxt のディレクトリ構造とルーティングの関係

Nuxt では、pagesディレクトリ内のファイル構造が自動的にルーティング構造に変換されます。これがファイルベースルーティングの核心です。

まず、基本的なディレクトリ構造を確認してみましょう:

bashpages/
├── index.vue          → /
├── about.vue          → /about
├── users/
│   ├── index.vue      → /users
│   └── [id].vue       → /users/123
└── blog/
    ├── index.vue      → /blog
    └── [slug].vue     → /blog/my-first-post

この構造を見ると、ファイル名がそのまま URL パスになることがわかります。index.vueは特別な意味を持ち、ディレクトリのデフォルトページとして機能します。

実際にプロジェクトを作成して確認してみましょう:

bash# Nuxtプロジェクトの作成
$ yarn create nuxt-app my-nuxt-app
$ cd my-nuxt-app
$ yarn dev

pages ディレクトリの役割と命名規則

pagesディレクトリは、Nuxt アプリケーションの心臓部です。ここに配置されたファイルは自動的にルートとして認識され、Vue コンポーネントとしてレンダリングされます。

基本的な命名規則を理解しましょう:

静的ルート

  • about.vue​/​about
  • contact.vue​/​contact
  • products.vue​/​products

動的ルート

  • [id].vue​/​123/456` など
  • [slug].vue​/​my-post, ​/​another-post など

ネストしたルート

  • users​/​index.vue​/​users
  • users​/​[id].vue​/​users​/​123

実際のコード例を見てみましょう:

vue<!-- pages/index.vue -->
<template>
  <div>
    <h1>ホームページ</h1>
    <p>ようこそ、Nuxtの世界へ!</p>
  </div>
</template>

<script setup>
// ページのメタデータを設定
useHead([object Object] title:ホームページ,meta: [
    { name: 'description, content:Nuxtアプリケーションのホームページです' }
  ]
})
</script>

基本的なページ遷移の実装

静的ルートの作成方法

静的ルートは、最もシンプルなページ遷移の実装方法です。固定の URL でアクセスできるページを作成します。

まず、基本的な静的ページを作成してみましょう:

vue<!-- pages/about.vue -->
<template>
  <div class="about-page>
    <h1私たちについて</h1>
    <p>このページは静的ルートの例です。</p>
    <NuxtLink to="/>ホームに戻る</NuxtLink>
  </div>
</template>

<script setup>
// ページ固有のロジック
const pageTitle = ref(私たちについて')

// SEO対策
useHead({
  title: pageTitle.value,
  meta: [
    { name: 'description', content: '私たちの会社について詳しく紹介します' }
  ]
})
</script>

よくあるエラーと解決策

bash# エラー: Cannot find module ./pages/about.vue# 原因: ファイル名の大文字小文字が間違っている
# 解決策: ファイル名を正確に確認する

# エラー: [nuxt] [request error] Cannot read properties of undefined
# 原因: script setupでrefをimportしていない
# 解決策: import [object Object] ref } from vueを追加

動的ルート(パラメータ付き)の実装

動的ルートは、URL パラメータを受け取って動的にコンテンツを表示する仕組みです。ブログ記事や商品詳細ページなどで活用されます。

vue<!-- pages/users/[id].vue -->
<template>
  <div class=user-detail>    <h1>ユーザー詳細: [object Object][object Object] user?.name }}</h1<div v-if="pending">読み込み中...</div>
    <div v-else-if=error">エラーが発生しました</div>
    <div v-else>
      <p>ID: {{ $route.params.id }}</p>
      <p>名前: [object Object][object Object]user?.name }}</p>
      <p>メール: {[object Object] user?.email }}</p>
    </div>
  </div>
</template>

<script setup>
// ルートパラメータを取得
const route = useRoute()
const userId = route.params.id

// ユーザーデータを取得
const { data: user, pending, error } = await useFetch(`/api/users/${userId}`)

// エラーハンドリング
if (error.value) {
  throw createError([object Object]   statusCode: 404,
    statusMessage: 'ユーザーが見つかりません'
  })
}
</script>

動的ルートでよくあるエラー

bash# エラー: [nuxt] [request error] Cannot read properties of undefined (reading 'params')
# 原因: useRoute()をscript setupの外で呼び出している
# 解決策: script setup内でuseRoute()を呼び出す

# エラー: [nuxt] [request error] Cannot read properties of undefined (readingid# 原因: route.paramsがundefined
# 解決策: route.paramsの存在確認を追加

ネストしたルートの構築

ネストしたルートは、階層構造を持つページを作成する際に使用します。親子関係のあるコンテンツを整理できます。

vue<!-- pages/blog/index.vue -->
<template>
  <div class=blog-list>
    <h1ログ一覧</h1>
    <div class="posts">
      <article v-for="post in posts" :key="post.id" class=post">
        <h2>[object Object][object Object] post.title }}</h2>
        <p>{{ post.excerpt }}</p>
        <NuxtLink :to="`/blog/$[object Object]post.slug}`">続きを読む</NuxtLink>
      </article>
    </div>
  </div>
</template>

<script setup>
// ブログ記事一覧を取得
const { data: posts } = await useFetch('/api/posts)</script>
vue<!-- pages/blog/[slug].vue -->
<template>
  <div class=blog-post>
    <h1>{[object Object] post?.title }}</h1>
    <div class="meta>
      <span>投稿日: {{ formatDate(post?.createdAt) }}</span>
    </div>
    <div class=content" v-html=post?.content></div>
    <NuxtLink to=/blog>←ブログ一覧に戻る</NuxtLink>
  </div>
</template>

<script setup>
const route = useRoute()
const slug = route.params.slug

// 記事データを取得
const { data: post } = await useFetch(`/api/posts/${slug}`)

// 日付フォーマット関数
const formatDate = (date) => {
  return new Date(date).toLocaleDateString('ja-JP')
}
</script>

高度なページ遷移テクニック

ミドルウェアを使った遷移制御

ミドルウェアは、ページ遷移の前後に実行される処理を定義できます。認証チェックやログ出力などに活用されます。

javascript// middleware/auth.js
export default defineNuxtRouteMiddleware((to, from) =>[object Object]
  // ユーザーの認証状態をチェック
  const user = useUser()

  if (!user.value && to.path.startsWith('/admin')) [object Object]   // 未認証の場合はログインページにリダイレクト
    return navigateTo(/login')
  }

  // ログ出力
  console.log(`ページ遷移: ${from.path} → ${to.path}`)
})

ページレベルでミドルウェアを適用する場合:

vue<!-- pages/admin/dashboard.vue -->
<template>
  <div class="admin-dashboard>
    <h1理者ダッシュボード</h1>
    <p>認証済みユーザーのみアクセス可能</p>
  </div>
</template>

<script setup>
// このページにミドルウェアを適用
definePageMeta([object Object]middleware: ['auth']
})
</script>

ミドルウェアでよくあるエラー

bash# エラー: [nuxt] [request error] Cannot read properties of undefined (reading value)
# 原因: useUser()が未定義
# 解決策: composableを正しく定義する

# エラー: [nuxt] [request error] navigateTo is not defined
# 原因: navigateToをimportしていない
# 解決策: import { navigateTo } from #app を追加

ページ遷移のアニメーション実装

Nuxt では、ページ遷移時にアニメーションを追加できます。ユーザー体験を向上させる重要な要素です。

vue<!-- app.vue -->
<template>
  <div>
    <NuxtPage />
  </div>
</template>

<style>
/* ページ遷移アニメーション */
.page-enter-active,
.page-leave-active {
  transition: all 0.3se;
}

.page-enter-from {
  opacity: 0;
  transform: translateX(20x);
}

.page-leave-to {
  opacity: 0;
  transform: translateX(-20;
}
</style>

特定のページでアニメーションをカスタマイズする場合:

vue<!-- pages/about.vue -->
<template>
  <div class="about-page>
    <h1私たちについて</h1>
    <p>カスタムアニメーション付きページ</p>
  </div>
</template>

<script setup>
// ページ固有のアニメーション設定
definePageMeta([object Object]
  pageTransition: [object Object]    name:slide-fade',
    mode: out-in'
  }
})
</script>

<style scoped>
.slide-fade-enter-active,
.slide-fade-leave-active {
  transition: all 0.3 ease;
}

.slide-fade-enter-from {
  opacity: 0;
  transform: translateY(30x);
}

.slide-fade-leave-to {
  opacity: 0;
  transform: translateY(-30;
}
</style>

プログラムによる遷移制御

JavaScript からプログラム的にページ遷移を制御する方法を学びましょう。

vue<!-- pages/contact.vue -->
<template>
  <div class="contact-form">
    <h1>お問い合わせ</h1>
    <form @submit.prevent="handleSubmit">
      <input v-model=form.name" type=text placeholder="お名前" required>
      <input v-model="form.email" type="emailplaceholder=メールアドレス" required>
      <textarea v-model="form.messageplaceholder="メッセージ" required></textarea>
      <button type=submit :disabled="isSubmitting">
       [object Object][object Object] isSubmitting ?送信中...' : 送信する }}
      </button>
    </form>
  </div>
</template>

<script setup>
const router = useRouter()
const form = ref({
  name: ail:
  message:const isSubmitting = ref(false)

const handleSubmit = async () => {
  isSubmitting.value = true

  try [object Object]   // フォーム送信処理
    await $fetch(/api/contact,[object Object]     method: POST',
      body: form.value
    })

    // 成功時の遷移
    await navigateTo('/thank-you, {
      replace: true
    })
  } catch (error) {
    console.error('送信エラー:, error)
    // エラー時の処理
  } finally [object Object]isSubmitting.value = false
  }
}
</script>

プログラム遷移でよくあるエラー

bash# エラー: [nuxt] [request error] navigateTo is not a function
# 原因: navigateToを正しくimportしていない
# 解決策: import { navigateTo } from #app' を追加

# エラー: [nuxt] [request error] Cannot read properties of undefined (readingpush)
# 原因: useRouter()の使用方法が間違っている
# 解決策: const router = useRouter() で正しく取得

ファイルベースルーティングの応用パターン

レイアウトを使った共通 UI の実装

Nuxt のレイアウト機能を使って、共通の UI 要素を効率的に管理できます。

vue<!-- layouts/default.vue -->
<template>
  <div class="layout">
    <header class="header">
      <nav>
        <NuxtLink to="/">ホーム</NuxtLink>
        <NuxtLink to=/about">私たちについて</NuxtLink>
        <NuxtLink to="/blog">ブログ</NuxtLink>
        <NuxtLink to="/contact>お問い合わせ</NuxtLink>
      </nav>
    </header>

    <main class="main>      <slot />
    </main>

    <footer class=footer>
      <p>&copy;2024xt App. All rights reserved.</p>
    </footer>
  </div>
</template>

<style scoped>
.layout {
  min-height:100h;
  display: flex;
  flex-direction: column;
}

.header [object Object]
  background: #f8f9fa;
  padding: 1rem;
  border-bottom:1px solid #e9ecef;
}

.main {
  flex: 1
  padding: 2rem;
}

.footer[object Object]  background: #343a40;
  color: white;
  padding: 1rem;
  text-align: center;
}
</style>

特定のページでカスタムレイアウトを使用する場合:

vue<!-- layouts/admin.vue -->
<template>
  <div class="admin-layout">
    <aside class=sidebar">
      <nav>
        <NuxtLink to="/admin/dashboard">ダッシュボード</NuxtLink>
        <NuxtLink to=/admin/users>ユーザー管理</NuxtLink>
        <NuxtLink to="/admin/posts>記事管理</NuxtLink>
      </nav>
    </aside>

    <main class=content>      <slot />
    </main>
  </div>
</template>
vue<!-- pages/admin/dashboard.vue -->
<template>
  <div class=dashboard>
    <h1理者ダッシュボード</h1>
    <p>管理画面専用レイアウトを使用しています</p>
  </div>
</template>

<script setup>
// カスタムレイアウトを指定
definePageMeta([object Object]  layout: 'admin'
})
</script>

エラーページのカスタマイズ

Nuxt では、エラーページをカスタマイズして、ユーザー体験を向上させることができます。

vue<!-- error.vue -->
<template>
  <div class="error-page">
    <div class="error-content">
      <h1>{{ error.statusCode }}</h1>
      <h2>{{ error.statusMessage }}</h2>
      <p>{{ error.message }}</p>

      <div class=actions>
        <button @click=handleError>ホームに戻る</button>
        <button @click="goBack>前のページに戻る</button>
      </div>
    </div>
  </div>
</template>

<script setup>
const error = useError()

const handleError = () =>[object Object]
  // エラーをクリアしてホームページに遷移
  clearError({ redirect: /})
}

const goBack = () =>[object Object]  // ブラウザの戻るボタンと同じ動作
  if (process.client) {
    window.history.back()
  }
}
</script>

<style scoped>
.error-page[object Object]
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100text-align: center;
}

.error-content h1
  font-size: 6m;
  color: #dc3545;
  margin: 0actions[object Object]  margin-top:2}

.actions button[object Object]  margin: 0 0.5rem;
  padding: 0.5rem 1rem;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}
</style>

SEO 対応のためのメタデータ設定

SEO 対策は、Web アプリケーションの成功に不可欠です。Nuxt では、ページごとにメタデータを設定できます。

vue<!-- pages/blog/[slug].vue -->
<template>
  <div class=blog-post>
    <h1>{[object Object] post?.title }}</h1>
    <div class=content" v-html=post?.content></div>
  </div>
</template>

<script setup>
const route = useRoute()
const slug = route.params.slug

// 記事データを取得
const { data: post } = await useFetch(`/api/posts/${slug}`)

// 動的なメタデータ設定
useHead({
  title: post.value?.title || '記事が見つかりません,meta: [
    { name: 'description', content: post.value?.excerpt || '' },
    { property: 'og:title', content: post.value?.title || '' },
    { property:og:description', content: post.value?.excerpt || '' },
    { property: 'og:image', content: post.value?.featuredImage || },  { name: 'twitter:card', content: 'summary_large_image' }
  ],
  link:
   [object Object] rel: canonical, href: `https://example.com/blog/${slug}` }
  ]
})
</script>

パフォーマンス最適化

遅延読み込み(Lazy Loading)の実装

Nuxt では、ページを遅延読み込みすることで、初期ロード時間を短縮できます。

vue<!-- pages/heavy-page.vue -->
<template>
  <div class="heavy-page>
    <h1>重いコンテンツのページ</h1>
    <div class="content">
      <!-- 大量のコンテンツ -->
    </div>
  </div>
</template>

<script setup>
// 遅延読み込みを有効にする
definePageMeta({
  lazy: true
})

// 重い処理を遅延実行
const heavyData = await $fetch(/api/heavy-data,[object Object]  lazy: true
})
</script>

プリフェッチの活用

プリフェッチ機能を使って、ユーザーがリンクにホバーした時に次のページを事前に読み込みます。

vue<!-- pages/index.vue -->
<template>
  <div class="home">
    <h1>ホームページ</h1>
    <div class="navigation>
      <!-- プリフェッチを有効にする -->
      <NuxtLink to=/aboutprefetch>私たちについて</NuxtLink>
      <NuxtLink to="/blogprefetch>ブログ</NuxtLink>
      <NuxtLink to="/contact" prefetch>お問い合わせ</NuxtLink>
    </div>
  </div>
</template>

特定の条件下でのみプリフェッチを有効にする場合:

vue<!-- pages/blog/index.vue -->
<template>
  <div class=blog-list>
    <h1ログ一覧</h1>
    <div class="posts">
      <article v-for="post in posts" :key="post.id" class=post">
        <h2>[object Object][object Object] post.title }}</h2>
        <p>{{ post.excerpt }}</p>
        <!-- 条件付きプリフェッチ -->
        <NuxtLink
          :to="`/blog/${post.slug}`"
          :prefetch="post.isPopular"
        >
          続きを読む
        </NuxtLink>
      </article>
    </div>
  </div>
</template>

<script setup>
const { data: posts } = await useFetch('/api/posts)</script>

キャッシュ戦略

Nuxt のキャッシュ機能を活用して、パフォーマンスを向上させましょう。

vue<!-- pages/products/[id].vue -->
<template>
  <div class=product-detail">
    <h1product?.name }}</h1>
    <p>{{ product?.description }}</p>
    <p>価格: ¥{{ product?.price }}</p>
  </div>
</template>

<script setup>
const route = useRoute()
const productId = route.params.id

// キャッシュ付きでデータを取得
const[object Object] data: product } = await useFetch(`/api/products/${productId}`, [object Object]  key: `product-${productId}`,
  default: () => null,
  // 5シュ
  server: false,
  lazy: true
})

// ページレベルでのキャッシュ設定
definePageMeta({
  keepalive: true
})
</script>

パフォーマンス最適化でよくあるエラー

error# エラー: [nuxt] [request error] Cannot read properties of undefined (reading value)
# 原因: キャッシュキーが正しく設定されていない
# 解決策: ユニークなキーを設定する

# エラー: [nuxt] [request error] prefetch is not a function
# 原因: prefetchの使用方法が間違っている
# 解決策: NuxtLinkコンポーネントでprefetchプロパティを使用

まとめ

Nuxt のファイルベースルーティングは、開発効率を劇的に向上させる革新的な仕組みです。この記事で学んだ内容を実践することで、保守性が高く、ユーザー体験の優れた Web アプリケーションを構築できるようになります。

重要なポイントを振り返ると:1. ファイル構造がそのまま URL 構造になる - 直感的で理解しやすい 2. 動的ルートで柔軟なページ作成 - パラメータを活用した動的コンテンツ 3 ミドルウェアで遷移制御 - 認証やログ出力などの処理を自動化 4. アニメーションで UX 向上 - スムーズなページ遷移でユーザー体験を向上 5. SEO 対策とパフォーマンス最適化 - 検索エンジン対応と高速化

実際のプロジェクトでは、これらの機能を組み合わせることで、より高度なアプリケーションを構築できます。エラーが発生した場合は、この記事で紹介した解決策を参考に、一つずつ問題を解決していきましょう。

Nuxt のファイルベースルーティングをマスターすれば、開発の世界が大きく広がります。ぜひ実践的なプロジェクトで活用してみてください。

関連リンク