Vue Router 動的ルーティング完全ガイド

2025-07-31

はじめに

Vue.jsのルーティングシステムにおいて、動的ルーティングは現代的なWebアプリケーション開発に欠かせない機能です。この記事では、Vue Routerの動的ルーティングについて、基本概念から高度な活用方法まで、実践的な例を交えて詳しく解説します。

動的ルーティングとは?

動的ルーティングとは、URLの一部を変数として扱い、同じコンポーネントで異なるコンテンツを表示する仕組みです。例えば、ユーザーIDや商品IDなど、変化する値をURLに含めることで、柔軟なページ設計が可能になります。

基本的な動的ルーティングの設定

動的セグメントの定義

動的ルーティングを設定するには、ルートパスにコロン(:)で始まるセグメントを追加します。

const routes = [
  {
    path: '/user/:id',
    component: UserProfile
  }
]

パラメータへのアクセス方法

動的セグメントの値には、以下の2つの方法でアクセスできます。

  1. $routeオブジェクト経由:
this.$route.params.id
  1. propsとして受け取る(推奨):
const routes = [
  {
    path: '/user/:id',
    component: UserProfile,
    props: true
  }
]

// UserProfile.vue
export default {
  props: ['id']
}

複数の動的セグメント

複数の動的セグメントを組み合わせることも可能です。

{
  path: '/category/:categoryId/product/:productId',
  component: ProductDetail
}

動的ルーティングの高度な使い方

正規表現による制約

動的セグメントに正規表現を適用して、マッチするパターンを制限できます。

{
  // 数値のIDのみマッチ
  path: '/user/:id(\\d+)',
  component: UserProfile
}

オプショナルな動的セグメント

セグメントの最後に?を追加すると、オプショナルなパラメータになります。

{
  // /user と /user/:id の両方にマッチ
  path: '/user/:id?',
  component: UserProfile
}

ワイルドカードルーティング

アスタリスク(*)を使用すると、任意のパスにマッチさせることができます。

{
  path: '/docs/*',
  component: Documentation
}

動的ルーティングの実践例

ユーザープロフィールページ

const routes = [
  {
    path: '/user/:userId',
    name: 'user-profile',
    component: UserProfile,
    props: true,
    meta: { requiresAuth: true }
  }
]
<!-- UserProfile.vue -->
<template>
  <div v-if="user">
    <h1>{{ user.name }}のプロフィール</h1>
    <p>ユーザーID: {{ userId }}</p>
    <!-- その他のユーザー情報 -->
  </div>
  <div v-else>
    <p>ユーザーが見つかりません</p>
  </div>
</template>

<script>
export default {
  props: ['userId'],
  data() {
    return {
      user: null
    }
  },
  async created() {
    try {
      const response = await fetch(`/api/users/${this.userId}`)
      this.user = await response.json()
    } catch (error) {
      console.error('ユーザー情報の取得に失敗しました:', error)
    }
  },
  watch: {
    userId() {
      // 同じコンポーネント内でuserIdが変わった場合の処理
      this.fetchUserData()
    }
  }
}
</script>

商品詳細ページ

{
  path: '/products/:category/:productSlug',
  component: ProductDetail,
  props: route => ({
    category: route.params.category,
    slug: route.params.productSlug,
    highlight: route.query.highlight // クエリパラメータもpropsとして渡す
  })
}

動的ルーティングの注意点

コンポーネントの再利用問題

同じコンポーネントが異なるルートで使用される場合、Vueは効率化のためにコンポーネントを再利用しようとします。これにより、ライフサイクルフックが再実行されないことがあります。

解決方法:

  1. watch$routeオブジェクトを監視
  2. beforeRouteUpdateナビゲーションガードを使用
export default {
  watch: {
    '$route'(to, from) {
      // ルートが変更された時の処理
      this.fetchData(to.params.id)
    }
  },
  methods: {
    fetchData(id) {
      // データ取得処理
    }
  }
}

スクロール位置の保持

動的ルーティングを使用すると、ブラウザのスクロール位置が保持されることがあります。これを防ぐには、ルーターの設定でスクロール動作を制御します。

const router = createRouter({
  history: createWebHistory(),
  routes,
  scrollBehavior(to, from, savedPosition) {
    if (savedPosition) {
      return savedPosition
    } else {
      return { top: 0 }
    }
  }
})

動的ルーティングのベストプラクティス

  1. propsモードを使用する: $routeに直接依存するよりも、propsとして受け取る方がコンポーネントの再利用性が高まります。
  2. 適切な命名規則: パラメータ名は意味のあるものにしましょう(:userId vs :id)。
  3. バリデーションを実装: 不正なパラメータ値に対処する仕組みを作りましょう。
  4. エラーハンドリング: 存在しないリソースへのアクセスを適切に処理しましょう。
  5. ローディング状態: データ取得中のローディング状態を表示しましょう。

高度なパターン

動的ルートの遅延読み込み

const routes = [
  {
    path: '/admin/:section',
    component: () => import(`@/views/Admin${capitalizeFirstLetter(section)}.vue`),
    beforeEnter: (to) => {
      const section = to.params.section
      if (!['dashboard', 'users', 'settings'].includes(section)) {
        return '/admin/dashboard'
      }
    }
  }
]

function capitalizeFirstLetter(string) {
  return string.charAt(0).toUpperCase() + string.slice(1)
}

動的ネストルート

function generateProductRoutes() {
  return [
    {
      path: 'overview',
      component: () => import('@/views/ProductOverview.vue')
    },
    {
      path: 'specs',
      component: () => import('@/views/ProductSpecs.vue')
    },
    {
      path: 'reviews',
      component: () => import('@/views/ProductReviews.vue')
    }
  ]
}

const routes = [
  {
    path: '/product/:id',
    component: () => import('@/views/ProductLayout.vue'),
    children: generateProductRoutes()
  }
]

パフォーマンス最適化

データプリフェッチ

{
  path: '/article/:slug',
  component: ArticleDetail,
  meta: {
    prefetch: true
  }
}
router.beforeResolve(async (to) => {
  if (to.meta.prefetch) {
    await store.dispatch('fetchArticle', to.params.slug)
  }
})

ルートベースのコード分割

const routes = [
  {
    path: '/user/:id',
    component: () => import(/* webpackChunkName: "user" */ './views/UserProfile.vue')
  }
]

テスト戦略

動的ルーティングのテスト例(JestとVue Test Utilsを使用):

import { mount } from '@vue/test-utils'
import { createRouter, createWebHistory } from 'vue-router'
import UserProfile from '@/views/UserProfile.vue'

describe('UserProfile.vue', () => {
  const mockRoute = {
    params: {
      id: '123'
    }
  }

  const mockRouter = {
    push: jest.fn()
  }

  it('should fetch user data based on route param', async () => {
    const fetchUserMock = jest.fn(() => Promise.resolve({
      id: '123',
      name: 'テストユーザー'
    }))

    const wrapper = mount(UserProfile, {
      global: {
        mocks: {
          $route: mockRoute,
          $router: mockRouter,
          $api: {
            fetchUser: fetchUserMock
          }
        }
      },
      props: {
        id: '123'
      }
    })

    await wrapper.vm.$nextTick()
    expect(fetchUserMock).toHaveBeenCalledWith('123')
    expect(wrapper.text()).toContain('テストユーザー')
  })
})

よくあるエラーと解決方法

404エラーの処理

存在しない動的ルートへのアクセスを適切に処理します。

{
  path: '/:pathMatch(.*)*',
  name: 'NotFound',
  component: NotFound
}

パラメータの型不一致

// 問題: 数値が必要だが文字列が渡される
this.$route.params.id // "123" (文字列)

// 解決策
const id = parseInt(this.$route.params.id, 10)
if (isNaN(id)) {
  // エラーハンドリング
}

まとめ

Vue Routerの動的ルーティングは、現代的なWebアプリケーション開発において不可欠な機能です。適切に実装することで:

  • 柔軟でスケーラブルなルーティングシステムを構築
  • 同じコンポーネントで異なるコンテンツを表示
  • URL設計を直感的でRESTfulなものに
  • アプリケーションの状態をURLで表現

が可能になります。

動的ルーティングをマスターすることで、より複雑で大規模なアプリケーションも効率的に開発できるようになります。実際のプロジェクトでこれらのテクニックを試しながら、理解を深めていきましょう。

さらに学ぶために