404 Not Found

404 Not Found


nginx

Vue 3 および Vue Router 4 のルーティング:`createRouter`、動的ルーティング、ネストされたルート、および遅延読み込み

Vue Router は Vue の公式の ルーティングマネージャー です。これにより、SPA(シングルページアプリケーション)に複数の「ページ」を持たせることができ、各 URL が 1 つのコンポーネントに対応するようになります。Vue Router 4 は Vue 3 向けに設計されたバージョンであり、Composition API を完全にサポートしています。

SPAのルーティングの中核をなすのは、URL ↔ コンポーネントのマッピングです。Vue Router 4を習得することは、ある程度の複雑さを持つVueアプリケーションを開発する上で不可欠なスキルです。

1. 学習内容


2. シングルページアプリケーションの「404の悪夢」

(1) 課題:5ページ、重複コードが5箇所

Aliceの管理画面には、5つのページ(ホーム/製品/注文/ユーザー/設定)がありました:

VUE
<!-- ❌ The "Flip" Version: Use v-if to switch 5 components -->
<template>
  <div>
    <button @click="currentPage = 'home'">Home</button>
    <button @click="currentPage = 'products'">Products</button>
    <button @click="currentPage = 'orders'">Orders</button>
    
    <div v-if="currentPage === 'home'"><HomePage /></div>
    <div v-else-if="currentPage === 'products'"><ProductsPage /></div>
    <div v-else-if="currentPage === 'orders'"><OrdersPage /></div>
    <div v-else>Page not found</div>
  </div>
</template>

4つの質問:

(2) Vue Router 4 の解決策:5つのルート + 5つのコンポーネント

JS
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'

const routes = [
  { path: '/', component: () => import('@/views/HomePage.vue') },
  { path: '/products', component: () => import('@/views/ProductsPage.vue') },
  { path: '/orders', component: () => import('@/views/OrdersPage.vue') },
  { path: '/users', component: () => import('@/views/UsersPage.vue') },
  { path: '/settings', component: () => import('@/views/SettingsPage.vue') }
]

const router = createRouter({
  history: createWebHistory(),
  routes
})

export default router
VUE
<!-- App.vue -->
<template>
  <nav>
    <router-link to="/">Home</router-link>
    <router-link to="/products">Products</router-link>
    <router-link to="/orders">Orders</router-link>
  </nav>
  
  <!-- ✅ Key:router-view Render the component for the current route -->
  <router-view />
</template>

5つのルート + 1つのルータービュー = 完全なSPA。URLは自動的に同期され、ブラウザの「進む」ボタンと「戻る」ボタンも期待通りに動作します。

(3) 収益

Vue Router を使用した後は:


3. ルートの初期化

(1) 初期化コードを完成させる

JS
// src/router/index.js
import { createRouter, createWebHistory, createWebHashHistory } from 'vue-router'

// 1. Define Routes(Lazy Loading)
const routes = [
  { path: '/', component: () => import('@/views/HomePage.vue') },
  { path: '/products', component: () => import('@/views/ProductsPage.vue') }
]

// 2. Create router Examples
const router = createRouter({
  // history Mode: URL without # (Recommended)
  history: createWebHistory(),
  // hash Mode: URL with # (Compatibility with older browsers)
  // history: createWebHashHistory(),
  
  routes,
  
  // Scrolling Behavior:Scroll to the top when switching routes
  scrollBehavior(to, from, savedPosition) {
    return savedPosition || { top: 0 }
  }
})

export default router

(2) main.js の登録

JS
// src/main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'

const app = createApp(App)
app.use(router)  // Registration Routes
app.mount('#app')

(3) 2つの主要な歴史モデルの比較

属性 createWebHistory createWebHashHistory
URLの形式 /products /#/products
SEO ✅ SEO対策済み ❌ SEO対策未対応
サーバー設定 必須 (try_files) 必須ではない
ブラウザの互換性 IE10以降 IE8以降
評価 ⭐⭐⭐⭐⭐ ⭐⭐

4. 5種類のルーティング設定

(1) 静的ルーティング

JS
const routes = [
  { path: '/', component: () => import('@/views/HomePage.vue') },
  { path: '/about', component: () => import('@/views/AboutPage.vue') },
  { path: '/contact', component: () => import('@/views/ContactPage.vue') }
]

(2) 動的ルーティング(パラメータ)

JS
const routes = [
  // /products/123
  { 
    path: '/products/:id', 
    component: () => import('@/views/ProductDetailPage.vue') 
  },
  // /users/456/posts/789
  { 
    path: '/users/:userId/posts/:postId', 
    component: () => import('@/views/PostPage.vue') 
  },
  // Optional Parameters
  { 
    path: '/news/:category?', 
    component: () => import('@/views/NewsPage.vue') 
  }
]
VUE
<!-- ProductDetailPage.vue Receive Parameters -->
<script setup>
import { useRoute } from 'vue-router'

const route = useRoute()
console.log(route.params.id)  // '123'
</script>

(3) ネストされたルート

JS
const routes = [
  {
    path: '/users',
    component: () => import('@/views/UsersPage.vue'),
    children: [
      {
        path: '',  // Default Subroute
        component: () => import('@/views/UsersListPage.vue')
      },
      {
        path: ':id',  // /users/123
        component: () => import('@/views/UserDetailPage.vue')
      }
    ]
  }
]
VUE
<!-- UsersPage.vue -->
<template>
  <div>
    <h1>Users</h1>
    <router-view />  <!-- Subnet Route Outbound -->
  </div>
</template>

(4) 名前付きルーティング

JS
const routes = [
  { 
    path: '/products/:id', 
    name: 'product-detail',  // Named Routing
    component: () => import('@/views/ProductDetailPage.vue') 
  }
]
VUE
<!-- Navigate via name, Avoid hard-coding URL -->
<router-link :to="{ name: 'product-detail', params: { id: 123 } }">
  Product 123
</router-link>

(5) ワイルドカード 404

JS
const routes = [
  { path: '/', component: () => import('@/views/HomePage.vue') },
  // ... Other Routes
  {
    path: '/:pathMatch(.*)*',  // Match all undefined routes
    component: () => import('@/views/NotFoundPage.vue')
  }
]

5. 5種類のナビゲーション

VUE
<template>
  <!-- String Path -->
  <router-link to="/products">Products</router-link>
  
  <!-- Named Routing(Recommendations) -->
  <router-link :to="{ name: 'products' }">Products</router-link>
  
  <!-- Dynamic Parameters -->
  <router-link :to="{ name: 'product-detail', params: { id: 123 } }">
    Product 123
  </router-link>
  
  <!-- Query Parameters -->
  <router-link :to="{ path: '/search', query: { q: 'vue' } }">
    Search
  </router-link>
</template>

(2) プログラムによるナビゲーション (router.push)

JS
import { useRouter } from 'vue-router'

const router = useRouter()

function goToProduct(id) {
  router.push(`/products/${id}`)  // String
  // or
  router.push({ name: 'product-detail', params: { id } })  // Object
}

function goBack() {
  router.back()  // Back
}

function goForward() {
  router.forward()  // Forward
}

function replaceCurrent() {
  router.replace('/login')  // Replace(Leaving No Trace of History)
}

(3) 「push」と「replace」の5つの違い

メソッド 動作 履歴
router.push() 新しいページへ このページを保存(戻ることができます)
router.replace() 現在のページを置き換える 痕跡を残さない(戻ることができない)
router.go(n) nステップ前進/後退 履歴を表示
router.back() 一歩引いて考える 歴史を探る
router.forward() 一歩前進 歴史を歩む

(4) 手続き型プログラミングと宣言型プログラミングの比較

シナリオ 推奨事項
ユーザーがリンクをクリックする router-link(宣言型)
JSによるナビゲーション router.push(プログラムによる呼び出し)
フォーム送信後のリダイレクト router.push
ログイン後のリダイレクト router.replace
404 自動リダイレクト router.back

6. ルートガード(ナビゲーションガード)

(1) ガードの3種類

JS
// 1. Global Frontline Guard(Most Commonly Used)
router.beforeEach((to, from, next) => {
  const isLoggedIn = !!localStorage.getItem('token')
  
  if (to.meta.requiresAuth && !isLoggedIn) {
    next('/login')  // Skip Login
  } else {
    next()  // Clearance
  }
})

// 2. Dedicated Route Guard
const routes = [
  {
    path: '/admin',
    component: () => import('@/views/AdminPage.vue'),
    beforeEnter: (to, from, next) => {
      if (isAdmin()) next()
      else next('/403')
    }
  }
]

// 3. In-Component Guard
// Within the component:beforeRouteEnter / beforeRouteUpdate / beforeRouteLeave

(2) ログイン保護の完全な例

JS
// router/index.js
router.beforeEach((to, from, next) => {
  const token = localStorage.getItem('token')
  const isLoggedIn = !!token
  
  // Pages that require login
  if (to.meta.requiresAuth && !isLoggedIn) {
    return next({ 
      name: 'login', 
      query: { redirect: to.fullPath }  // Redirect after logging in
    })
  }
  
  // Already logged in. Go to the login page.
  if (to.name === 'login' && isLoggedIn) {
    return next({ name: 'home' })
  }
  
  next()
})
JS
// Routing meta Layout
const routes = [
  {
    path: '/dashboard',
    component: () => import('@/views/DashboardPage.vue'),
    meta: { requiresAuth: true }  // You must log in
  }
]

7. 完全な例:Eコマースのバックエンドルーティングシステム

(1) ▶ サンプル:. 5種類のルーティング設定

JS
const routes = [
  // 1. Static Routing
  { path: '/', name: 'home', component: () => import('@/views/HomePage.vue') },
  
  // 2. Dynamic Routing
  { 
    path: '/products/:id', 
    name: 'product-detail',
    component: () => import('@/views/ProductDetailPage.vue') 
  },
  
  // 3. Nested Routes
  {
    path: '/admin',
    component: () => import('@/views/AdminLayout.vue'),
    meta: { requiresAuth: true },
    children: [
      { path: '', name: 'admin-dashboard', component: () => import('@/views/DashboardPage.vue') },
      { path: 'products', name: 'admin-products', component: () => import('@/views/AdminProductsPage.vue') },
      { path: 'orders', name: 'admin-orders', component: () => import('@/views/AdminOrdersPage.vue') }
    ]
  },
  
  // 4. Named Routing
  { 
    path: '/user/:userId', 
    name: 'user-profile',
    component: () => import('@/views/UserProfilePage.vue') 
  },
  
  // 5. 404
  { path: '/:pathMatch(.*)*', name: 'not-found', component: () => import('@/views/NotFoundPage.vue') }
]
▶ 試してみよう

(2) ▶ サンプル:. ルートの遅延読み込み(5つのコンポーネントのオンデマンド読み込み)

JS
const routes = [
  // ✅ Recommendations:News import
  { path: '/', component: () => import('@/views/HomePage.vue') },
  { path: '/products', component: () => import('@/views/ProductsPage.vue') }
]
▶ 試してみよう

(3) ▶ サンプル:. ナビゲーションの5つの種類

VUE
<!-- 1. String Path -->
<router-link to="/products">Products</router-link>

<!-- 2. Named Routing(Recommendations)-->
<router-link :to="{ name: 'products' }">Products</router-link>

<!-- 3. Dynamic Parameters -->
<router-link :to="{ name: 'product-detail', params: { id: 123 } }">
  Product 123
</router-link>

<!-- 4. Query Parameters -->
<router-link :to="{ path: '/search', query: { q: 'vue' } }">
  Search
</router-link>

<!-- 5. Programmatic Navigation -->
<button @click="goToProduct(123)">Go to Product</button>
▶ 試してみよう
JS
import { useRouter } from 'vue-router'
const router = useRouter()
function goToProduct(id) {
  router.push({ name: 'product-detail', params: { id } })
}

(4) ▶ サンプル:. ログインガードの完成

JS
router.beforeEach((to, from, next) => {
  const token = localStorage.getItem('token')
  const isLoggedIn = !!token
  
  if (to.meta.requiresAuth && !isLoggedIn) {
    return next({ name: 'login', query: { redirect: to.fullPath } })
  }
  
  if (to.name === 'login' && isLoggedIn) {
    return next({ name: 'home' })
  }
  
  next()
})
▶ 試してみよう

(5) ▶ サンプル:. よくある5つの間違いに関するクイックリファレンス

エラー 症状 解決策
ルータービューが表示されていません ページが表示されません <router-view /> を追加
ルートは遅延読み込みされない 最初の画面の読み込みが遅い 動的インポートには () => import() を使用
Infinite Loop 抜け出せない next() が beforeEach を再度呼び出さないようにする
出口のないネストされたルーティング サブページが表示されない 親コンポーネントに <router-view /> を追加
404 ページが見つかりません エラー path: '/:pathMatch(.*)*' を追加

(6) ▶ サンプル:. 5つの主要なパフォーマンス比較

モード 最初の画面 切り替え 適用対象
静的インポート 遅い 速い 小規模なプロジェクト
動的インポート ⭐⭐⭐⭐⭐ 初回読み込みに時間がかかる 推奨
ルーティンググループの遅延読み込み ⭐⭐⭐⭐⭐ 初回読み込み時の速度低下 大規模なプロジェクト
プリフェッチ ⭐⭐⭐⭐ 高速 ユーザー体験を最優先
SSR ⭐⭐⭐⭐⭐ 高速 Nuxt 3

❓ よくある質問

Q createWebHistorycreateWebHashHistory、どちらを選べばいいですか?
A createWebHistory#を含まないURL、SEOに最適)はサーバーの設定が必要です。createWebHashHistory#を含むURL)は古いブラウザにも対応しています。本番環境では createWebHistory を推奨します。
Q ダイナミックルーティングはどのようにパラメータを受け取るのですか?
A useRoute() フックを使用します:const route = useRoute(); route.params.id
Q router.pushrouter.replace の違いは何ですか?
A push はブラウザの履歴に追加されます(ユーザーが「戻る」操作を行えるようになります)が、replace は現在のページを上書きします(履歴は残りません)。ログイン後は replace を使用してください(ユーザーはログインページに戻ることができないため)。
Q ルーターガードは非同期で実行できますか?
A はい。beforeEach(async (to, from, next) => { await checkAuth(); next() })。ただし、無限ループにならないよう注意してください。
Q ネストされたルーティングはどのように実装しますか?
A children: [] を使用して親ルート内で子ルートを設定し、子ルートの出口として <router-view /> を親コンポーネントのテンプレートに追加します。
Q Vue Router 4 と Vue Router 3 の違いは何ですか?
A Vue Router 4 は Composition API(useRouter/useRoute)に対応しており、より強力な TypeScript の型推論を提供し、ナビゲーションガードのパラメータ(to/from/next)が変更されています。Vue 3 プロジェクトでは Vue Router 4 を使用する必要があります。
Q レイジーローディングはパフォーマンスをどの程度向上させますか?
A (まだ閲覧されていないページを読み込まないことで)最初の画面の読み込み時間を50~80%短縮できます。この効果は、大規模なプロジェクト(10ページ以上)で特に顕著です。
Q ルートガードでメタフィールドを使用するにはどうすればよいですか?
A ルート設定に meta: { requiresAuth: true } を追加し、ガード内で to.meta.requiresAuth を使用してください。これらは、権限、タイトル、ブレッドクラムなどに使用されます。

📖 まとめ


📝 練習問題

(1) 基礎問題(難易度:⭐)

シンプルな複数ページ構成のSPAを実装する:

(2) 上級問題(難易度:⭐⭐)

Eコマースにおける商品ルーティングの実装:

(3) チャレンジ問題(難易度:⭐⭐⭐)

完全なeコマース用バックエンドルーティングシステムを実装する:

  1. 10ページ以上(ホーム/商品/注文/ユーザー/設定/ログイン/404など)
  2. ネストされたルート(/admin/dashboard、/admin/products など)
  3. ルートの遅延読み込み(最初の画面の読み込みを高速化)
  4. 警備員としてログインしてください(ログインしていない場合は /login にアクセスし、ログイン済みの場合はこのページに戻ってください)
  5. 5つのナビゲーション手法を網羅
  6. プログラムによるナビゲーション(ボタンをクリックして移動)
  7. TypeScript による強型付けルーティングの設定
Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%