Как устроен роутинг во Vue с помощью Vue Router?
Вопрос
Как определяются маршруты в Vue Router, как работают динамические параметры и что такое навигационные guard'ы?
Короткий ответ
Vue Router сопоставляет URL с компонентами через массив объектов routes (path, component, вложенные children). Динамические сегменты пути (:id) попадают в route.params и доступны компоненту через useRoute(). Навигационные guard'ы (beforeEach — глобальный, beforeEnter — для конкретного маршрута) — это функции-middleware, которые выполняются перед переходом и могут разрешить, отменить или перенаправить навигацию.
Подробный ответ
Определение маршрутов. Роутер создаётся через createRouter с массивом routes, где каждый маршрут — это path (шаблон URL) и component (что рендерить). Вложенные маршруты задаются через children, что позволяет строить layout'ы с вложенным <router-view>.
Динамические параметры. Сегмент пути, начинающийся с :, — это параметр: /users/:id сматчит /users/42 и положит { id: '42' } в route.params. Внутри компонента параметры читаются через useRoute().params (Composition API) — это реактивный объект, то есть при переходе с /users/42 на /users/43 (тот же компонент переиспользуется) params.id обновится реактивно, и это стоит учитывать: хуки жизненного цикла компонента не перевызываются при таком переходе, нужно явно наблюдать за route.params через watch.
Навигационные guard'ы — функции, вызываемые на разных этапах перехода между маршрутами:
- Глобальный
router.beforeEach((to, from) => { ... })— выполняется перед каждым переходом. Типичное применение — проверка авторизации: если пользователь не залогинен, а маршрут требует авторизации, вернутьfalse(отменить переход) или объект маршрута для редиректа ({ name: 'login' }}). beforeEnter— guard, привязанный к конкретному маршруту в его определении, срабатывает только при входе на этот маршрут.- Есть также guard'ы уровня компонента (
onBeforeRouteLeave,onBeforeRouteUpdateв Composition API) и глобальныеbeforeResolve/afterEach.
Guard может вернуть: true/undefined — разрешить переход, false — отменить, строку/объект пути — сделать редирект.
Пример
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
import UserView from '../views/UserView.vue'
import LoginView from '../views/LoginView.vue'
const routes = [
{ path: '/', name: 'home', component: HomeView },
{
path: '/users/:id',
name: 'user',
component: UserView,
meta: { requiresAuth: true }
},
{ path: '/login', name: 'login', component: LoginView }
]
const router = createRouter({
history: createWebHistory(),
routes
})
// глобальный guard: проверка авторизации перед каждым переходом
router.beforeEach((to) => {
const isAuthenticated = Boolean(localStorage.getItem('token'))
if (to.meta.requiresAuth && !isAuthenticated) {
return { name: 'login' } // редирект вместо разрешения перехода
}
})
export default router
<!-- UserView.vue: чтение динамического параметра -->
<script setup>
import { watch } from 'vue'
import { useRoute } from 'vue-router'
const route = useRoute()
// компонент переиспользуется при переходе /users/1 -> /users/2,
// поэтому параметр нужно отслеживать через watch
watch(() => route.params.id, (id) => {
console.log('Загружаем пользователя:', id)
}, { immediate: true })
</script>
<template>
<p>ID пользователя: {{ route.params.id }}</p>
</template>
Дополнительные вопросы
- Почему при переходе между маршрутами с одним и тем же компонентом хуки жизненного цикла не вызываются заново?
- Чем
createWebHistoryотличается отcreateWebHashHistory? - Как организовать ленивую загрузку компонентов маршрутов (
component: () => import(...))?