Vue Router — маршрутизация
Vue Router — официальная библиотека маршрутизации для Vue. Настройка роутов, динамические параметры, nested routes, guards, lazy loading и программная навигация.
Установка
npm install vue-router
В проекте, созданном через create-vue, роутер уже установлен.
Базовая настройка
// router/index.ts
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'home',
component: () => import('@/views/HomeView.vue'),
},
{
path: '/about',
name: 'about',
component: () => import('@/views/AboutView.vue'),
},
],
})
export default router
Подключение в main.ts:
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
createApp(App).use(router).mount('#app')
В шаблоне — <RouterView /> для рендера текущего роута и <RouterLink> для навигации:
<template>
<nav>
<RouterLink to="/">Главная</RouterLink>
<RouterLink to="/about">О проекте</RouterLink>
</nav>
<RouterView />
</template>
<RouterLink> становится тегом <a>, но работает без перезагрузки страницы. У активного роута автоматически добавляется класс router-link-active.
Динамические роуты
Параметры в пути обозначаются через ::
{
path: '/users/:id',
name: 'user',
component: () => import('@/views/UserView.vue'),
}
Доступ к параметрам в компоненте:
<script setup lang="ts">
import { useRoute } from 'vue-router'
const route = useRoute()
const userId = route.params.id // string | string[]
</script>
Необязательный параметр:
{ path: '/users/:id/:tab?' }
Повторяющийся параметр:
{ path: '/files/:path(.*)*' } // /files/a/b/c → path = ['a', 'b', 'c']
Nested Routes (вложенные роуты)
Вложенные роуты позволяют создавать компоненты с вложенным <RouterView>:
{
path: '/dashboard',
component: () => import('@/views/DashboardLayout.vue'),
children: [
{
path: '', // /dashboard
component: () => import('@/views/DashboardHome.vue'),
},
{
path: 'settings', // /dashboard/settings
component: () => import('@/views/DashboardSettings.vue'),
},
{
path: 'profile', // /dashboard/profile
component: () => import('@/views/DashboardProfile.vue'),
},
],
}
В DashboardLayout.vue:
<template>
<aside>
<RouterLink to="/dashboard">Обзор</RouterLink>
<RouterLink to="/dashboard/settings">Настройки</RouterLink>
<RouterLink to="/dashboard/profile">Профиль</RouterLink>
</aside>
<main>
<RouterView />
</main>
</template>
Программная навигация
import { useRouter } from 'vue-router'
const router = useRouter()
// Переход по имени роута
router.push({ name: 'user', params: { id: '42' } })
// Переход по пути
router.push('/about')
// С query-параметрами
router.push({ path: '/search', query: { q: 'vue', page: '1' } })
// Замена текущей записи в истории
router.replace({ name: 'home' })
// Переход назад/вперёд
router.go(-1)
router.go(1)
Navigation Guards
Глобальные
Вызываются при каждом переходе:
router.beforeEach((to, from) => {
const auth = useAuthStore()
if (to.meta.requiresAuth && !auth.isLoggedIn) {
return { name: 'login', query: { redirect: to.fullPath } }
}
})
router.afterEach((to, from) => {
document.title = to.meta.title ?? 'Моё приложение'
})
Per-route guards
Привязаны к конкретному роуту:
{
path: '/admin',
component: () => import('@/views/AdminView.vue'),
beforeEnter: (to, from) => {
if (!isAdmin()) return { name: 'home' }
},
}
In-component guards
Прямо в компоненте через <script setup>:
<script setup lang="ts">
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'
onBeforeRouteLeave((to, from) => {
const hasChanges = false // проверка несохранённых данных
if (hasChanges) {
const answer = confirm('Есть несохранённые изменения. Уйти?')
if (!answer) return false
}
})
onBeforeRouteUpdate(async (to, from) => {
// Вызывается при смене параметров того же роута
// /users/1 → /users/2
await fetchUser(to.params.id)
})
</script>
Lazy Loading
Загрузка компонентов по необходимости — сокращает размер начального бандла:
// Ленивая загрузка (рекомендуется)
{
path: '/about',
component: () => import('@/views/AboutView.vue'),
}
// Eager-загрузка (для критичных роутов)
import HomeView from '@/views/HomeView.vue'
{
path: '/',
component: HomeView,
}
Группировка чанков:
{
path: '/admin',
component: () => import(/* webpackChunkName: "admin" */ '@/views/AdminView.vue'),
}
// Vite: именованные чанки
{
path: '/admin',
component: () => import('@/views/AdminView.vue'),
}
// Vite автоматически группирует файлы из одной директории
Route Meta
Метаданные роута — произвольные данные, доступные в guards и компонентах:
declare module 'vue-router' {
interface RouteMeta {
requiresAuth?: boolean
title?: string
permissions?: string[]
}
}
const routes = [
{
path: '/dashboard',
component: () => import('@/views/DashboardView.vue'),
meta: {
requiresAuth: true,
title: 'Панель управления',
permissions: ['dashboard:read'],
},
},
]
Типизация RouteMeta через augmentation (как выше) даёт автодополнение.
404 и редиректы
const routes = [
// Редирект
{ path: '/home', redirect: '/' },
{ path: '/old-page', redirect: { name: 'new-page' } },
// 404 — ловит все неизвестные роуты
{
path: '/:pathMatch(.*)*',
name: 'not-found',
component: () => import('@/views/NotFoundView.vue'),
},
]
Query-параметры
router.push({ path: '/search', query: { q: 'vue', sort: 'date' } })
В компоненте:
const route = useRoute()
const query = route.query.q // string | string[]
const sort = route.query.sort // string | string[]
Отслеживание изменений query:
watch(() => route.query, (newQuery) => {
search(newQuery.q)
})
useLink
Composition API для <RouterLink>:
<script setup lang="ts">
import { RouterLink, useLink } from 'vue-router'
const props = defineProps<{ to: string }>()
const { route, href, isActive, isExactActive, navigate } = useLink(props)
</script>
<template>
<a :href="href" @click="navigate" :class="{ active: isActive }">
<slot />
</a>
</template>
Scroll Behavior
Контроль позиции прокрутки при переходе:
const router = createRouter({
history: createWebHistory(),
routes,
scrollBehavior(to, from, savedPosition) {
if (savedPosition) return savedPosition
if (to.hash) return { el: to.hash, behavior: 'smooth' }
return { top: 0 }
},
})
Практический пример
Полная конфигурация роутера с guards и meta:
import { createRouter, createWebHistory } from 'vue-router'
import { useAuthStore } from '@/stores/auth'
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/',
component: () => import('@/views/HomeView.vue'),
},
{
path: '/login',
name: 'login',
component: () => import('@/views/LoginView.vue'),
meta: { guestOnly: true },
},
{
path: '/dashboard',
component: () => import('@/views/DashboardLayout.vue'),
meta: { requiresAuth: true },
children: [
{ path: '', name: 'dashboard', component: () => import('@/views/DashboardHome.vue') },
{ path: 'settings', name: 'settings', component: () => import('@/views/SettingsView.vue') },
],
},
{
path: '/:pathMatch(.*)*',
name: 'not-found',
component: () => import('@/views/NotFoundView.vue'),
},
],
scrollBehavior(to, _, savedPosition) {
return savedPosition ?? { top: 0 }
},
})
router.beforeEach((to) => {
const auth = useAuthStore()
if (to.meta.requiresAuth && !auth.isLoggedIn) {
return { name: 'login', query: { redirect: to.fullPath } }
}
if (to.meta.guestOnly && auth.isLoggedIn) {
return { name: 'dashboard' }
}
})
router.afterEach((to) => {
document.title = `${to.meta.title ?? 'Приложение'} | Мой сайт`
})
export default router
Итог
Vue Router — полноценная библиотека маршрутизации. Основные возможности: динамические параметры, вложенные роуты, guards для защиты переходов, lazy loading для оптимизации бандла, meta-поля для хранения данных роута. Используйте useRoute() для доступа к параметрам и useRouter() для программной навигации.