この内容は古いバージョンです。最新バージョンを表示するには、戻るボタンを押してください。
バージョン:4
ページ更新者:T
更新日時:2026-06-11 07:07:02

タイトル: ルーティング設定
SEOタイトル: Vue.js ルーティング (Vue Router) 完全ガイド

この本文には実行可能なタグ等が含まれるため、安全のためHTMLソースとして表示しています。本文データは変更していません。

<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;background:#fffbea;border:1px solid #f4c430;margin:1em 0;width:100%"><tr><td style="background:#f4c430;color:#000;font-weight:bold;padding:8px">この記事の要点</td></tr><tr><td style="padding:10px"><ul><li><strong>Vue Router</strong> は Vue.js 公式の SPA ルーティングライブラリ</li><li>Vue 3 では <code>vue-router@4</code>、Vue 2 では <code>vue-router@3</code> を使用</li><li>基本: <code>createRouter()</code> + <code>createWebHistory()</code> + <code>routes</code> 配列を <code>app.use(router)</code></li><li>テンプレートでは <strong><router-view></strong> で表示、<strong><router-link to="/"></strong> で遷移</li><li>動的セグメント <code>/user/:id</code> は <code>useRoute().params.id</code> で取得、Lazy Loading は <code>() => import()</code></li></ul></td></tr></table>
<h2>Vue Router とは</h2>

<p><strong>Vue Router</strong> は Vue.js 公式のクライアントサイドルーティングライブラリです。SPA (Single Page Application) において、URL と Vue コンポーネントを対応付け、ページ遷移時にコンポーネントを切り替えます。サーバーへのリクエストは発生せず、ブラウザの History API でアドレスバーを書き換えます。</p>

<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;width:100%">
<thead><tr style="background:#e3f2fd"><th>Vue バージョン</th><th>Vue Router バージョン</th><th>主な API</th></tr></thead>
<tbody>
<tr><td>Vue 3</td><td>vue-router@4.x</td><td><code>createRouter</code> / <code>createWebHistory</code></td></tr>
<tr><td>Vue 2</td><td>vue-router@3.x</td><td><code>new VueRouter()</code> / <code>mode: 'history'</code></td></tr>
</tbody>
</table>

<h2>インストール</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-bash"># Vue 3 の場合
npm install vue-router@4

# Vue 2 の場合
npm install vue-router@3

# Vite で新規作成時はオプションで含められる
npm create vue@latest
# → "Add Vue Router for Single Page Application development?" → Yes</code></pre>
<h2>基本セットアップ (Vue 3 + Composition API)</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">// src/router/index.js
import { createRouter, createWebHistory } from &apos;vue-router&apos;;
import Home from &apos;@/views/Home.vue&apos;;
import About from &apos;@/views/About.vue&apos;;

const routes = [
  { path: &apos;/&apos;, name: &apos;home&apos;, component: Home },
  { path: &apos;/about&apos;, name: &apos;about&apos;, component: About },
  // Lazy Loading(コード分割)
  { path: &apos;/contact&apos;, component: () => import(&apos;@/views/Contact.vue&apos;) },
];

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes,
});

export default router;</code></pre><pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">// src/main.js
import { createApp } from &apos;vue&apos;;
import App from &apos;./App.vue&apos;;
import router from &apos;./router&apos;;

createApp(App)
  .use(router)
  .mount(&apos;#app&apos;);</code></pre>
<h2>テンプレートでの利用</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html"><!-- App.vue -->
<template>
  <nav>
    <router-link to="/">Home</router-link> |
    <router-link to="/about">About</router-link> |
    <router-link :to="{ name: &apos;contact&apos; }">Contact</router-link>
  </nav>

  <!-- ここに各ルートのコンポーネントが表示される -->
  <router-view />
</template></code></pre>
<p><code><router-link></code> は内部的に <code><a href></code> を生成しますが、クリック時に History API を使うため、ページリロードは発生しません。アクティブな状態には自動で <code>router-link-active</code> / <code>router-link-exact-active</code> クラスが付きます。</p>

<h2>history モードの違い</h2>

<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;width:100%">
<thead><tr style="background:#e3f2fd"><th>モード</th><th>URL 例</th><th>サーバ設定</th></tr></thead>
<tbody>
<tr><td><code>createWebHistory()</code></td><td><code>/about</code></td><td>★必須: 全 URL を index.html にフォールバック</td></tr>
<tr><td><code>createWebHashHistory()</code></td><td><code>/#/about</code></td><td>不要(# 以降はサーバへ送られない)</td></tr>
<tr><td><code>createMemoryHistory()</code></td><td>なし</td><td>SSR / テスト用</td></tr>
</tbody>
</table>

<h2>動的セグメント (URL パラメータ)</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">// router/index.js
const routes = [
  { path: &apos;/user/:id&apos;, name: &apos;user&apos;, component: User },
  // 複数のパラメータ
  { path: &apos;/post/:category/:slug&apos;, component: Post },
  // オプショナル + 正規表現
  { path: &apos;/items/:id(\\d+)?&apos;, component: Items },  // 数字のみ、省略可
];</code></pre><pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html"><!-- User.vue -->
<script setup>
import { useRoute, useRouter } from &apos;vue-router&apos;;
import { watch } from &apos;vue&apos;;

const route = useRoute();    // 現在のルート情報(params, query, path, name)
const router = useRouter();  // ルーター本体(push, replace, back)

console.log(route.params.id);   // /user/42 → "42"
console.log(route.query.tab);   // /user/42?tab=posts → "posts"

// パラメータ変更を監視(同じコンポーネントの使い回し時に必須)
watch(() => route.params.id, (newId, oldId) => {
  console.log(`id changed: ${oldId} → ${newId}`);
  // 再フェッチ等
});

function goHome() {
  router.push(&apos;/&apos;);                      // パス指定
  router.push({ name: &apos;home&apos; });         // ルート名指定
  router.push({ name: &apos;user&apos;, params: { id: 99 } });
  router.replace(&apos;/about&apos;);              // 履歴に残さない
  router.back();                         // 戻る
}
</script>

<template>
  <div>User ID: {{ route.params.id }}</div>
</template></code></pre>
<h2>ネストルート</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">const routes = [
  {
    path: &apos;/user/:id&apos;,
    component: User,
    children: [
      // /user/:id/profile
      { path: &apos;profile&apos;, component: UserProfile },
      // /user/:id/posts
      { path: &apos;posts&apos;, component: UserPosts },
      // /user/:id(デフォルト子ルート)
      { path: &apos;&apos;, component: UserHome },
    ],
  },
];</code></pre>
<p>親コンポーネント側でも <code><router-view /></code> を置くと、その位置に子ルートのコンポーネントが描画されます。</p>

<h2>ナビゲーションガード</h2>

<p>ルート遷移の前後にフックを差し込み、認証チェックやリダイレクトを実装できます。</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">// グローバルガード
router.beforeEach((to, from, next) => {
  if (to.meta.requiresAuth && !isLoggedIn()) {
    next({ name: &apos;login&apos;, query: { redirect: to.fullPath } });
  } else {
    next();
  }
});

// 解決後(次画面のコンポーネントロード後)
router.beforeResolve(async (to) => {
  if (to.meta.fetchOnEnter) {
    await loadData(to);
  }
});

router.afterEach((to, from) => {
  // 画面遷移後のアナリティクス送信など
  gtag(&apos;event&apos;, &apos;page_view&apos;, { page_path: to.fullPath });
});

// ルート別ガード
const routes = [
  {
    path: &apos;/admin&apos;,
    component: Admin,
    meta: { requiresAuth: true },
    beforeEnter: (to, from) => { /* ... */ },
  },
];</code></pre>
<h2>Lazy Loading (コード分割)</h2>

<p>初期バンドルサイズを抑えるには、各ルートのコンポーネントを動的 import します:</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">const routes = [
  // ❌ 同期 import: 初期バンドルに全部入る
  { path: &apos;/heavy&apos;, component: HeavyView },

  // ✅ 動的 import: 訪問時に別チャンクをロード
  { path: &apos;/heavy&apos;, component: () => import(&apos;@/views/HeavyView.vue&apos;) },

  // チャンク名を指定
  {
    path: &apos;/dashboard&apos;,
    component: () => import(/* webpackChunkName: "dashboard" */ &apos;@/views/Dashboard.vue&apos;),
  },
];</code></pre>
<h2>Vue 2 (vue-router 3.x) との違い</h2>

<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;width:100%">
<thead><tr style="background:#fff3e0"><th>項目</th><th>Vue 2 / vue-router 3</th><th>Vue 3 / vue-router 4</th></tr></thead>
<tbody>
<tr><td>作成</td><td><code>new VueRouter({ routes })</code></td><td><code>createRouter({ routes, history })</code></td></tr>
<tr><td>history</td><td><code>mode: 'history'</code></td><td><code>history: createWebHistory()</code></td></tr>
<tr><td>登録</td><td><code>new Vue({ router })</code></td><td><code>app.use(router)</code></td></tr>
<tr><td>取得 (Composition)</td><td><code>this.$route</code> / <code>this.$router</code></td><td><code>useRoute()</code> / <code>useRouter()</code></td></tr>
<tr><td>キャッチオール</td><td><code>path: '*'</code></td><td><code>path: '/:pathMatch(.*)*'</code></td></tr>
</tbody>
</table>

<h2>404 ページの設定</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">const routes = [
  // ... 他のルート
  // 最後に: マッチしないものを全部ここへ
  { path: &apos;/:pathMatch(.*)*&apos;, name: &apos;not-found&apos;, component: NotFound },
];</code></pre>
<h2>FAQ</h2>

<p><strong>Q: リロードすると 404 になる</strong><br />
A: <code>createWebHistory</code> モードはサーバ側で「全 URL を index.html にフォールバック」設定が必要です。Nginx なら <code>try_files $uri $uri/ /index.html;</code>、Laravel なら <code>Route::view('/{any}', 'app')->where('any', '.*');</code>。</p>

<p><strong>Q: 同じコンポーネントでパラメータだけ変わったときに再生成されない</strong><br />
A: Vue Router の意図的な最適化です。<code>watch(() => route.params, ...)</code> で監視するか、<code><router-view :key="$route.fullPath" /></code> で強制再生成します。</p>

<p><strong>Q: Vue Router で SSR したい</strong><br />
A: Nuxt 3 を使うのが最短です。手動なら <code>createMemoryHistory()</code> + サーバで <code>router.push()</code> → <code>router.isReady()</code> 待ち → レンダリングのフローになります。</p>