← Vue Menu · ← Prev: Composition API
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)
// 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'
}
}
<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>
// 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>
// Route with named views
{
path: '/',
components: {
default: MainContent,
sidebar: Sidebar,
header: Header,
}
}
// App.vue
<RouterView /> <!-- default -->
<RouterView name="sidebar" />
<RouterView name="header" />
<!-- 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>
| Task | Code |
|---|---|
| Navigate to named route | router.push({ name: 'home' }) |
| Navigate with params | router.push({ name: 'detail', params: { id: 5 } }) |
| Navigate with query | router.push({ path: '/search', query: { q: 'vue' } }) |
| Replace (no history entry) | router.replace('/home') |
| Go back | router.back() |
| Get current params | route.params.id |
| Get current query | route.query.search |
| Get current route name | route.name |
| Active link class | <RouterLink active-class="active"> |
| Lazy load component | () => import('@/pages/Foo.vue') |