🏠 Home / Hub

🧭 Vue 14 — Vue Router

← Vue Menu · ← Prev: Composition API

1. Install & Setup

npm install vue-router

# src/router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import { useAuthStore } from '@/stores/useAuthStore'

// Lazy loading (split code — load only when needed)
const HomePage     = () => import('@/pages/HomePage.vue')
const ProductsPage = () => import('@/pages/ProductsPage.vue')
const ProductDetail = () => import('@/pages/ProductDetailPage.vue')
const LoginPage    = () => import('@/pages/LoginPage.vue')
const NotFoundPage = () => import('@/pages/NotFoundPage.vue')

const routes = [
  { path: '/',              name: 'home',       component: HomePage },
  { path: '/login',         name: 'login',      component: LoginPage },
  {
    path: '/products',
    name: 'products',
    component: ProductsPage,
    meta: { requiresAuth: true },  // guarded route
  },
  {
    path: '/products/:id',
    name: 'product-detail',
    component: ProductDetail,
    meta: { requiresAuth: true },
  },
  { path: '/:pathMatch(.*)*', name: 'not-found', component: NotFoundPage },
]

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes,
  scrollBehavior: () => ({ top: 0 }),  // scroll to top on navigation
})

export default router

# main.js
app.use(router)

2. Route Guards (Auth Protection)

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

  if (to.meta.requiresAuth && !auth.isLoggedIn) {
    next({ name: 'login', query: { redirect: to.fullPath } })
  } else if (to.name === 'login' && auth.isLoggedIn) {
    next({ name: 'home' })   // already logged in → redirect home
  } else {
    next()   // proceed
  }
})

// After login — redirect to intended page
const redirectPath = route.query.redirect || '/'
router.push(redirectPath)

// Per-route guard (in route definition)
{
  path: '/admin',
  component: AdminPage,
  beforeEnter: (to, from) => {
    const auth = useAuthStore()
    if (!auth.isAdmin) return '/403'
  }
}

3. Using Router in Components

<script setup>
import { useRouter, useRoute } from 'vue-router'

const router = useRouter()   // programmatic navigation
const route  = useRoute()    // current route info

// Read route params / query
const productId = route.params.id         // /products/42 → '42'
const search    = route.query.q           // /products?q=phone → 'phone'
const pageName  = route.name              // 'products'

// Navigate programmatically
function goToProduct(id) {
  router.push({ name: 'product-detail', params: { id } })
}
function goBack() {
  router.back()
}
function goWithQuery() {
  router.push({ path: '/products', query: { q: search.value, page: 2 } })
}
function replaceRoute() {
  router.replace('/login')   // replace (no back button entry)
}
</script>

<template>
  <!-- Declarative navigation -->
  <RouterLink to="/">Home</RouterLink>
  <RouterLink :to="{ name: 'products' }" active-class="active">Products</RouterLink>
  <RouterLink :to="{ name: 'product-detail', params: { id: 5 } }">Product 5</RouterLink>

  <!-- Where matched component renders -->
  <RouterView />
</template>

4. Nested Routes

// routes with children
{
  path: '/dashboard',
  component: DashboardLayout,  // has <RouterView/> inside
  children: [
    { path: '',           name: 'dashboard',          component: DashboardHome },
    { path: 'products',   name: 'dashboard-products', component: DashboardProducts },
    { path: 'settings',   name: 'dashboard-settings', component: DashboardSettings },
  ]
}

// DashboardLayout.vue
<template>
  <div class="dashboard">
    <DashboardSidebar />
    <main>
      <RouterView />  <!-- nested child renders here -->
    </main>
  </div>
</template>

5. Named Views (Multiple <RouterView>)

// Route with named views
{
  path: '/',
  components: {
    default: MainContent,
    sidebar: Sidebar,
    header: Header,
  }
}

// App.vue
<RouterView />                    <!-- default -->
<RouterView name="sidebar" />
<RouterView name="header" />

6. Dynamic Route with Data Fetch

<!-- ProductDetailPage.vue -->
<script setup>
import { ref, watch } from 'vue'
import { useRoute }   from 'vue-router'

const route   = useRoute()
const product = ref(null)
const loading = ref(false)

async function load(id) {
  loading.value  = true
  product.value  = null
  try {
    const res = await fetch(`/api/products/${id}`)
    product.value = await res.json()
  } finally {
    loading.value = false
  }
}

// Load on mount AND when ID param changes (/products/1 → /products/2)
watch(() => route.params.id, load, { immediate: true })
</script>

<template>
  <div v-if="loading">Loading...</div>
  <div v-else-if="product">
    <h1>{{ product.name }}</h1>
    <p>{{ product.description }}</p>
  </div>
</template>

7. Router Quick Reference

TaskCode
Navigate to named routerouter.push({ name: 'home' })
Navigate with paramsrouter.push({ name: 'detail', params: { id: 5 } })
Navigate with queryrouter.push({ path: '/search', query: { q: 'vue' } })
Replace (no history entry)router.replace('/home')
Go backrouter.back()
Get current paramsroute.params.id
Get current queryroute.query.search
Get current route nameroute.name
Active link class<RouterLink active-class="active">
Lazy load component() => import('@/pages/Foo.vue')

📌 Study Checklist