Как устроен роутинг во Vue с помощью Vue Router?

MiddleVue.js #vue #vue-router #routing

Вопрос

Как определяются маршруты в 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(...))?