はじめに
Vue.jsのルーティングシステムにおいて、動的ルーティングは現代的なWebアプリケーション開発に欠かせない機能です。この記事では、Vue Routerの動的ルーティングについて、基本概念から高度な活用方法まで、実践的な例を交えて詳しく解説します。
動的ルーティングとは?
動的ルーティングとは、URLの一部を変数として扱い、同じコンポーネントで異なるコンテンツを表示する仕組みです。例えば、ユーザーIDや商品IDなど、変化する値をURLに含めることで、柔軟なページ設計が可能になります。

基本的な動的ルーティングの設定
動的セグメントの定義
動的ルーティングを設定するには、ルートパスにコロン(:)で始まるセグメントを追加します。
const routes = [
{
path: '/user/:id',
component: UserProfile
}
]
パラメータへのアクセス方法
動的セグメントの値には、以下の2つの方法でアクセスできます。
$routeオブジェクト経由:
this.$route.params.id
- 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は効率化のためにコンポーネントを再利用しようとします。これにより、ライフサイクルフックが再実行されないことがあります。
解決方法:
watchで$routeオブジェクトを監視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 }
}
}
})
動的ルーティングのベストプラクティス
動的ルーティングを実装する際は、保守性と使いやすさを意識することが重要です。
コンポーネントは$routeに直接依存するのではなく、propsでパラメータを受け取ることで再利用しやすくなります。また、パラメータ名は意味が分かる名前を付け、不正な値に備えたバリデーションやエラーハンドリングを実装しましょう。さらに、データ取得中はローディング画面を表示することで、ユーザーに分かりやすい画面遷移を提供できます。
高度なパターン
動的ルートの遅延読み込み
管理画面のURLに応じて必要なコンポーネントだけを動的に読み込む例です。また、beforeEnterでルートパラメータを確認し、許可されていないセクションが指定された場合はダッシュボードへリダイレクトすることで、不正なアクセスを防いでいます。
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)
}
動的ネストルート
商品詳細ページの子ルートを関数で生成する例です。overview、specs、reviewsといった画面を親ルートの下にまとめて定義することで、ルーティングを整理し、拡張しやすい構成を実現しています。
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()
}
]
パフォーマンス最適化
データプリフェッチ
ページ遷移前に必要なデータを取得する仕組みです。meta.prefetchが設定されたルートでは、beforeResolveで記事データを取得してから画面を表示するため、表示速度やユーザー体験を向上させることができます。
{
path: '/article/:slug',
component: ArticleDetail,
meta: {
prefetch: true
}
}
router.beforeResolve(async (to) => {
if (to.meta.prefetch) {
await store.dispatch('fetchArticle', to.params.slug)
}
})
ルートベースのコード分割
ルートごとにJavaScriptファイルを分割して読み込む例です。ユーザーがそのページへアクセスしたときだけコンポーネントを読み込むため、初回表示を高速化し、アプリケーション全体のパフォーマンスを向上できます。
const routes = [
{
path: '/user/:id',
component: () => import(/* webpackChunkName: "user" */ './views/UserProfile.vue')
}
]
テスト戦略
動的ルーティングのテスト例(JestとVue Test Utilsを使用)です。JestとVue Test Utilsを使用して動的ルーティングをテストする例です。ルートパラメータやAPIをモック化し、正しいユーザー情報が取得されることや、取得したデータが画面に表示されることを確認しています。
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エラーの処理
存在しない動的ルートへのアクセスを適切に処理します。定義されていないURLへアクセスした場合に404ページを表示するためのルーティング設定です。/:pathMatch(.)ですべての未定義のパスを受け取り、NotFoundコンポーネントを表示することで、存在しないページへのアクセスを適切に案内できます。
{
path: '/:pathMatch(.*)*',
name: 'NotFound',
component: NotFound
}
パラメータの型不一致
Vue Routerで取得するルートパラメータは、数値に見えても文字列として取得されます。そのため、数値として利用する場合はparseInt()で数値へ変換し、isNaN()で変換に失敗していないか確認することで、安全にデータを扱うことができます。
// 問題: 数値が必要だが文字列が渡される
this.$route.params.id // "123" (文字列)
// 解決策
const id = parseInt(this.$route.params.id, 10)
if (isNaN(id)) {
// エラーハンドリング
}
まとめ
Vue Routerの動的ルーティングを利用すると、URLに応じて表示するコンテンツを柔軟に切り替えられるため、拡張性の高いWebアプリケーションを構築できます。
同じコンポーネントを複数のページで再利用できるほか、RESTfulで分かりやすいURL設計や、アプリケーションの状態をURLで表現することも可能です。動的ルーティングを習得することで、大規模なアプリケーションでも効率よく開発できるようになるため、実際のプロジェクトで活用しながら理解を深めていきましょう。