Vue Router — официальная библиотека маршрутизации для Vue. Она превращает Vue-приложение в **SPA (Single Page Application)**: переходы между страницами происходят без перезагрузки браузера через History API.
import { createRouter, createWebHistory } from 'vue-router'
import Home from './pages/Home.vue'
import About from './pages/About.vue'
const router = createRouter({
history: createWebHistory(), // использует HTML5 History API
routes: [
{ path: '/', component: Home },
{ path: '/about', component: About },
{ path: '/:pathMatch(.*)*', component: NotFound }, // 404
]
})
// Подключить к приложению
const app = createApp(App)
app.use(router)
app.mount('#app')<!-- App.vue — корневой компонент -->
<template>
<nav>
<!-- router-link создаёт <a> без перезагрузки страницы -->
<router-link to="/">Главная</router-link>
<router-link to="/about">О нас</router-link>
<router-link to="/users/42">Профиль</router-link>
</nav>
<!-- Здесь рендерится компонент текущего маршрута -->
<router-view />
</template>// Маршрут с параметрами
{ path: '/users/:id', component: UserProfile }
{ path: '/posts/:year/:month', component: PostList }
// В компоненте:
import { useRoute } from 'vue-router'
const route = useRoute()
console.log(route.params.id) // '42'
console.log(route.params.year) // '2024'// URL: /search?query=vue&page=2
const route = useRoute()
console.log(route.query.query) // 'vue'
console.log(route.query.page) // '2'
// Переход с query params
const router = useRouter()
router.push({ path: '/search', query: { query: 'pinia', page: 1 } })import { useRouter } from 'vue-router'
const router = useRouter()
// Переход
router.push('/about')
router.push({ name: 'user', params: { id: 42 } })
// Замена текущей записи истории (не добавляет в стек)
router.replace('/login')
// Навигация по истории
router.back() // назад
router.forward() // вперёд
router.go(-2) // на 2 шага назадХуки, вызываемые при переходах между маршрутами:
// Глобальный guard — выполняется перед каждым переходом
router.beforeEach((to, from) => {
const isAuthenticated = checkAuth()
if (to.meta.requiresAuth && !isAuthenticated) {
return '/login' // перенаправить
}
// return true или ничего — разрешить переход
})
// Guard на уровне маршрута
{
path: '/admin',
component: Admin,
meta: { requiresAuth: true },
beforeEnter: (to, from) => {
if (!isAdmin()) return '/403'
}
}
// Guard в компоненте
import { onBeforeRouteLeave } from 'vue-router'
onBeforeRouteLeave((to, from) => {
if (hasUnsavedChanges.value) {
return confirm('Уйти без сохранения?')
}
})| Режим | API | URL | Особенности |
|---|---|---|---|
| createWebHistory | HTML5 History | /users/42 | Нужна настройка сервера |
| createWebHashHistory | Hash | /#/users/42 | Работает без сервера |
| createMemoryHistory | В памяти | нет URL | SSR, тесты |
Простой SPA-роутер через History API — основа того, как работает Vue Router
// Минимальный SPA-роутер на основе History API.
// Показывает ключевые концепции: маршруты, динамические сегменты, guards.
class SimpleRouter {
constructor() {
this.routes = []
this.guards = []
this.currentPath = typeof window !== 'undefined' ? window.location.pathname : '/'
}
// Добавить маршрут
addRoute(path, handler) {
this.routes.push({ path, handler })
return this
}
// Добавить guard
beforeEach(guard) {
this.guards.push(guard)
return this
}
// Разобрать динамический маршрут
matchRoute(path) {
for (const route of this.routes) {
const params = this._matchPath(route.path, path)
if (params !== null) {
return { route, params }
}
}
return null
}
_matchPath(pattern, path) {
const patternParts = pattern.split('/')
const pathParts = path.split('/')
if (patternParts.length !== pathParts.length) return null
const params = {}
for (let i = 0; i < patternParts.length; i++) {
if (patternParts[i].startsWith(':')) {
// Динамический сегмент: ':id' матчит любое значение
const paramName = patternParts[i].slice(1)
params[paramName] = pathParts[i]
} else if (patternParts[i] !== pathParts[i]) {
return null // статические сегменты должны совпадать
}
}
return params
}
// Выполнить навигацию
async navigate(path) {
const from = this.currentPath
// Запустить guards
for (const guard of this.guards) {
const result = await guard({ path }, { path: from })
if (result === false) {
console.log(` [guard] Переход на ${path} заблокирован`)
return
}
if (typeof result === 'string') {
console.log(` [guard] Перенаправление: ${path} -> ${result}`)
return this.navigate(result)
}
}
// Найти маршрут
const match = this.matchRoute(path)
if (!match) {
console.log(`[404] Маршрут не найден: ${path}`)
return
}
// Обновить адресную строку (в реальном браузере)
// window.history.pushState({}, '', path)
this.currentPath = path
console.log(`[navigate] ${from} -> ${path}`)
if (Object.keys(match.params).length > 0) {
console.log(` params: `, match.params)
}
// Вызвать handler
match.route.handler({ path, params: match.params })
}
back() {
// В реальном браузере: window.history.back()
console.log('[router] back() — navigate history')
}
}
// --- Демонстрация ---
const router = new SimpleRouter()
router
.addRoute('/', () => console.log(' => Рендерим: Главная страница'))
.addRoute('/about', () => console.log(' => Рендерим: О нас'))
.addRoute('/users/:id', ({ params }) => {
console.log(` => Рендерим: Профиль пользователя #${params.id}`)
})
.addRoute('/posts/:year/:month', ({ params }) => {
console.log(` => Рендерим: Посты за ${params.month}/${params.year}`)
})
// Guard: только авторизованные пользователи
let isAuthenticated = false
router.beforeEach((to) => {
const protected_ = ['/admin', '/profile']
if (protected_.includes(to.path) && !isAuthenticated) {
return '/login' // перенаправить
}
})
// Тестируем навигацию
;(async () => {
await router.navigate('/')
await router.navigate('/about')
await router.navigate('/users/42')
await router.navigate('/posts/2024/03')
await router.navigate('/users/99')
await router.navigate('/not-found') // 404
})()Vue Router — официальная библиотека маршрутизации для Vue. Она превращает Vue-приложение в **SPA (Single Page Application)**: переходы между страницами происходят без перезагрузки браузера через History API.
import { createRouter, createWebHistory } from 'vue-router'
import Home from './pages/Home.vue'
import About from './pages/About.vue'
const router = createRouter({
history: createWebHistory(), // использует HTML5 History API
routes: [
{ path: '/', component: Home },
{ path: '/about', component: About },
{ path: '/:pathMatch(.*)*', component: NotFound }, // 404
]
})
// Подключить к приложению
const app = createApp(App)
app.use(router)
app.mount('#app')<!-- App.vue — корневой компонент -->
<template>
<nav>
<!-- router-link создаёт <a> без перезагрузки страницы -->
<router-link to="/">Главная</router-link>
<router-link to="/about">О нас</router-link>
<router-link to="/users/42">Профиль</router-link>
</nav>
<!-- Здесь рендерится компонент текущего маршрута -->
<router-view />
</template>// Маршрут с параметрами
{ path: '/users/:id', component: UserProfile }
{ path: '/posts/:year/:month', component: PostList }
// В компоненте:
import { useRoute } from 'vue-router'
const route = useRoute()
console.log(route.params.id) // '42'
console.log(route.params.year) // '2024'// URL: /search?query=vue&page=2
const route = useRoute()
console.log(route.query.query) // 'vue'
console.log(route.query.page) // '2'
// Переход с query params
const router = useRouter()
router.push({ path: '/search', query: { query: 'pinia', page: 1 } })import { useRouter } from 'vue-router'
const router = useRouter()
// Переход
router.push('/about')
router.push({ name: 'user', params: { id: 42 } })
// Замена текущей записи истории (не добавляет в стек)
router.replace('/login')
// Навигация по истории
router.back() // назад
router.forward() // вперёд
router.go(-2) // на 2 шага назадХуки, вызываемые при переходах между маршрутами:
// Глобальный guard — выполняется перед каждым переходом
router.beforeEach((to, from) => {
const isAuthenticated = checkAuth()
if (to.meta.requiresAuth && !isAuthenticated) {
return '/login' // перенаправить
}
// return true или ничего — разрешить переход
})
// Guard на уровне маршрута
{
path: '/admin',
component: Admin,
meta: { requiresAuth: true },
beforeEnter: (to, from) => {
if (!isAdmin()) return '/403'
}
}
// Guard в компоненте
import { onBeforeRouteLeave } from 'vue-router'
onBeforeRouteLeave((to, from) => {
if (hasUnsavedChanges.value) {
return confirm('Уйти без сохранения?')
}
})| Режим | API | URL | Особенности |
|---|---|---|---|
| createWebHistory | HTML5 History | /users/42 | Нужна настройка сервера |
| createWebHashHistory | Hash | /#/users/42 | Работает без сервера |
| createMemoryHistory | В памяти | нет URL | SSR, тесты |
Простой SPA-роутер через History API — основа того, как работает Vue Router
// Минимальный SPA-роутер на основе History API.
// Показывает ключевые концепции: маршруты, динамические сегменты, guards.
class SimpleRouter {
constructor() {
this.routes = []
this.guards = []
this.currentPath = typeof window !== 'undefined' ? window.location.pathname : '/'
}
// Добавить маршрут
addRoute(path, handler) {
this.routes.push({ path, handler })
return this
}
// Добавить guard
beforeEach(guard) {
this.guards.push(guard)
return this
}
// Разобрать динамический маршрут
matchRoute(path) {
for (const route of this.routes) {
const params = this._matchPath(route.path, path)
if (params !== null) {
return { route, params }
}
}
return null
}
_matchPath(pattern, path) {
const patternParts = pattern.split('/')
const pathParts = path.split('/')
if (patternParts.length !== pathParts.length) return null
const params = {}
for (let i = 0; i < patternParts.length; i++) {
if (patternParts[i].startsWith(':')) {
// Динамический сегмент: ':id' матчит любое значение
const paramName = patternParts[i].slice(1)
params[paramName] = pathParts[i]
} else if (patternParts[i] !== pathParts[i]) {
return null // статические сегменты должны совпадать
}
}
return params
}
// Выполнить навигацию
async navigate(path) {
const from = this.currentPath
// Запустить guards
for (const guard of this.guards) {
const result = await guard({ path }, { path: from })
if (result === false) {
console.log(` [guard] Переход на ${path} заблокирован`)
return
}
if (typeof result === 'string') {
console.log(` [guard] Перенаправление: ${path} -> ${result}`)
return this.navigate(result)
}
}
// Найти маршрут
const match = this.matchRoute(path)
if (!match) {
console.log(`[404] Маршрут не найден: ${path}`)
return
}
// Обновить адресную строку (в реальном браузере)
// window.history.pushState({}, '', path)
this.currentPath = path
console.log(`[navigate] ${from} -> ${path}`)
if (Object.keys(match.params).length > 0) {
console.log(` params: `, match.params)
}
// Вызвать handler
match.route.handler({ path, params: match.params })
}
back() {
// В реальном браузере: window.history.back()
console.log('[router] back() — navigate history')
}
}
// --- Демонстрация ---
const router = new SimpleRouter()
router
.addRoute('/', () => console.log(' => Рендерим: Главная страница'))
.addRoute('/about', () => console.log(' => Рендерим: О нас'))
.addRoute('/users/:id', ({ params }) => {
console.log(` => Рендерим: Профиль пользователя #${params.id}`)
})
.addRoute('/posts/:year/:month', ({ params }) => {
console.log(` => Рендерим: Посты за ${params.month}/${params.year}`)
})
// Guard: только авторизованные пользователи
let isAuthenticated = false
router.beforeEach((to) => {
const protected_ = ['/admin', '/profile']
if (protected_.includes(to.path) && !isAuthenticated) {
return '/login' // перенаправить
}
})
// Тестируем навигацию
;(async () => {
await router.navigate('/')
await router.navigate('/about')
await router.navigate('/users/42')
await router.navigate('/posts/2024/03')
await router.navigate('/users/99')
await router.navigate('/not-found') // 404
})()Реализуй класс `Router` с методами: `addRoute(path, handler)` — регистрирует маршрут, `navigate(path)` — вызывает handler совпавшего маршрута, `back()` — возвращает предыдущий путь из истории. Поддержи динамические сегменты: маршрут `/users/:id` должен матчить `/users/42` и передавать `{ id: "42" }` в handler вторым аргументом.
В _matchPath разбей оба пути через split('/'), сравни длины, затем итерируйся по сегментам: если pattern[i] начинается с ':', запиши pathParts[i] в params[paramName], иначе сравни строки. В back() pop() из истории убирает текущий, следующий pop() — предыдущий, который и нужно передать в navigate.
Токены для AI-помощника закончились
Купи токены чтобы задавать вопросы AI прямо в уроке