Chuyển đến nội dung chính

レッスン 5: Vue ルーターの詳細

Vue Router 4、ルート定義、動的ルート。ナビゲーション ガード (beforeEach、beforeRouteEnter)。遅延読み込み、ネストされたルート。スクロール動作、ルートメタ、ルート遷移。

💻 プログラミング — レッスン 5 レッスン 5: Vue ルーターの詳細

Vue.js と Nuxt: 基本から高度まで

パート 2: Vue エコシステム

xdev.asia

1.ルーターのセットアップ

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

const router = createRouter({
  history: createWebHistory(),
  routes: [
    {
      path: '/',
      name: 'home',
      component: () => import('@/pages/Home.vue'),
    },
    {
      path: '/blog',
      name: 'blog',
      component: () => import('@/pages/Blog.vue'),
      children: [
        {
          path: ':slug',
          name: 'blog-detail',
          component: () => import('@/pages/BlogDetail.vue'),
          props: true,
        },
      ],
    },
    {
      path: '/dashboard',
      component: () => import('@/layouts/DashboardLayout.vue'),
      meta: { requiresAuth: true },
      children: [
        { path: '', name: 'dashboard', component: () => import('@/pages/Dashboard.vue') },
        { path: 'settings', name: 'settings', component: () => import('@/pages/Settings.vue') },
      ],
    },
    {
      path: '/:pathMatch(.*)*',
      name: 'not-found',
      component: () => import('@/pages/NotFound.vue'),
    },
  ],
  scrollBehavior(to, from, savedPosition) {
    if (savedPosition) return savedPosition
    if (to.hash) return { el: to.hash, behavior: 'smooth' }
    return { top: 0 }
  },
})

export default router

2. 動的ルート

<!-- pages/BlogDetail.vue -->
<script setup lang="ts">
import { useRoute, useRouter } from 'vue-router'

const route = useRoute()
const router = useRouter()

// Với props: true
const props = defineProps<{ slug: string }>()

// Hoặc từ route params
const slug = route.params.slug as string

// Query params
const page = route.query.page as string // ?page=2

// Programmatic navigation
function goToPost(id: string) {
  router.push({ name: 'blog-detail', params: { slug: id } })
}

function goBack() {
  router.back()
}
</script>

3. ナビゲーションガード

// Global guard
router.beforeEach(async (to, from) => {
  const authStore = useAuthStore()

  if (to.meta.requiresAuth && !authStore.isAuthenticated) {
    return { name: 'login', query: { redirect: to.fullPath } }
  }

  if (to.meta.requiresAdmin && authStore.user?.role !== 'admin') {
    return { name: 'forbidden' }
  }
})

// Per-route guard
{
  path: '/admin',
  beforeEnter: (to, from) => {
    const auth = useAuthStore()
    if (auth.user?.role !== 'admin') return false
  },
}
<!-- In-component guard -->
<script setup lang="ts">
import { onBeforeRouteLeave } from 'vue-router'

const hasUnsavedChanges = ref(false)

onBeforeRouteLeave((to, from) => {
  if (hasUnsavedChanges.value) {
    return window.confirm('Bạn có thay đổi chưa lưu. Rời khỏi trang?')
  }
})
</script>

4. ルート遷移

<template>
  <RouterView v-slot="{ Component, route }">
    <Transition :name="route.meta.transition || 'fade'" mode="out-in">
      <component :is="Component" :key="route.path" />
    </Transition>
  </RouterView>
</template>

<style>
.fade-enter-active, .fade-leave-active {
  transition: opacity 0.3s ease;
}
.fade-enter-from, .fade-leave-to {
  opacity: 0;
}
.slide-enter-active, .slide-leave-active {
  transition: transform 0.3s ease;
}
.slide-enter-from { transform: translateX(100%); }
.slide-leave-to { transform: translateX(-100%); }
</style>

次の記事: Pinia による状態管理 — ストア、ゲッター、アクション、プラグイン。