Навигация по маршрутам
В этой главе вы научитесь использовать Vue Router для реализации навигации по страницам с разделением фронтенда и бэкенда, включая динамические параметры маршрута.
Зачем использовать маршрутизацию?
Традиционные многостраничные сайты запрашивают у сервера новый HTML-файл при клике по ссылке, вызывая полную перезагрузку страницы с мерцанием белого экрана.
Vue Router реализуетфронтенд-маршрутизацию: при изменении URL запрос на сервер не отправляется; вместо этого JS динамически переключает отображаемый компонент.
Преимущества такого подхода:Переключение страниц без перезагрузки, обеспечивающее плавную работу, как в нативном приложении。
Основные концепции Vue Router
| Концепция | Назначение | Соответствующий код |
|---|---|---|
| Таблица маршрутов | Определяет сопоставление между путями URL и компонентами | routesМассив |
| Точка вывода маршрутизатора | Место, где отображается совпавший компонент | <RouterView /> |
| Навигационная ссылка | Нажмите, чтобы перейти к указанному маршруту | <RouterLink to="..."> |
| Динамическая маршрутизация | URL содержит переменные (например, ID статьи) | /post/:id |
Установка и настройка
Если вы уже выбрали Router при создании проекта, можете пропустить этап установки.
Если вы его не выбрали, выполните в терминале:
$ npm install vue-router@4
Создайте файл конфигурации маршрутов
Пример
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
// Таблица маршрутов: каждая запись сопоставляет путь компоненту
const routes = [
{
path: '/', // Путь URL
name: 'home', // Имя маршрута (необязательно, используется при программной навигации)
component: HomeView // Соответствующий компонент страницы
},
{
// Динамический маршрут: :id — это заполнитель, который соответствует любому значению
// /post/1, /post/2, /post/hello все будут соответствовать этой записи
path: '/post/:id',
name: 'post',
// Ленивая загрузка: загружается только при обращении; при сборке выделяется отдельный JS-файл
component: () => import('../views/PostView.vue')
}
]
// Создайте экземпляр маршрутизатора
const router = createRouter({
history: createWebHistory(), // Используйте режим HTML5 History (URL выглядит как обычный путь)
routes // Зарегистрируйте таблицу маршрутов
})
export default router
Различия между двумя режимами маршрутизатора:createWebHistoryСоздаёт чистые URL (/post/1), требует поддержки конфигурации сервера. Если среда развёртывания неизвестна, можно использоватьcreateWebHashHistory, где URL содержит символ # (/#/post/1), конфигурация сервера не требуется.
Зарегистрируйте маршрутизатор в main.js
Пример
<script setup>
import NavBar from './components/NavBar.vue'
</script>
<template>
<NavBar />
<main>
<!-- RouterView — это волшебное место: компонент, сопоставленный текущему маршруту, будет отрендерен здесь -->
<RouterView />
</main>
</template>
RouterView— это основной выход системы маршрутизации.
Когда URL равен/, отображается HomeView; когда URL равен/post/1, отображается PostView.
RouterLink — декларативная навигация
RouterLink— это навигационный компонент, предоставляемый Vue Router, заменяющий традиционный<a>tag.
Он не вызывает перезагрузку страницы; он лишь переключает отображаемый компонент через JS.
Пример
<!-- Обычный синтаксис -->
<RouterLink to="/">Главная</RouterLink>
<!-- Синтаксис именованного маршрута (рекомендуется: только одно место для изменения при изменении пути) -->
<RouterLink :to="{ name: 'post', params: { id: article.id } }">
Читать статью {{ article.id }}
</RouterLink>
<!-- Традиционный тег a вызывает перезагрузку страницы, не используйте -->
<!-- <a href="/post/1">Читать полную статью</a> -->
</template>
useRoute — чтение параметров маршрута
На странице деталей PostView нужно знать ID статьи в URL, чтобы найти соответствующую статью.
useRouteиспользуется для чтения информации о текущем маршруте.
Пример
<script setup>
import { useRoute } from 'vue-router'
// useRoute возвращает объект текущего маршрута
const route = useRoute()
// route.params содержит параметры в URL
// Например, если URL — /post/3, то route.params.id === '3'
console.log('Article ID:', route.params.id)
</script>
<template>
<div>
<h1>Страница статьи</h1>
<!-- Примечание: значения в params — все строки -->
<p>Текущий ID статьи: {{ route.params.id }}</p>
</div>
</template>
route.params.idзначение всегда является строкой. Даже если URL равен/post/3, полученное значение —'3'не число3. Когда нужно число, используйтеNumber(route.params.id)илиparseIntдля преобразования.
Практика: завершите страницу сведений о статье
Сделайте BlogCard кликабельным для навигации, а страница сведений отображает полное содержимое статьи по ID.
Шаг 1: Измените BlogCard для поддержки навигации по клику
Пример
<script setup>
defineProps({
id: Number, // Новое: ID статьи для навигации
title: String,
summary: String,
date: String,
category: String
})
</script>
<template>
<!-- Вся карточка обёрнута в RouterLink, поэтому клик в любом месте выполняет навигацию -->
<RouterLink :to="{ name: 'post', params: { id } }" class="card-link">
<div class="card">
<span class="tag">{{ category }}</span>
<h3>{{ title }}</h3>
<p>{{ summary }}</p>
<span class="date">{{ date }}</span>
</div>
</RouterLink>
</template>
<style scoped>
.card-link { text-decoration: none; color: inherit; display: block; }
.card {
background: #fff;
border-radius: 12px;
padding: 20px;
box-shadow: 0 2px 12px rgba(0,0,0,0.08);
transition: transform 0.2s, box-shadow 0.2s;
}
.card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 24px rgba(0,0,0,0.12);
}
.tag {
display: inline-block;
padding: 2px 10px;
background: #E8EEF5;
color: #427DB8;
border-radius: 12px;
font-size: 12px;
}
.card h3 { margin: 10px 0 8px; font-size: 18px; }
.card p { color: #666; font-size: 14px; line-height: 1.6; }
.date { font-size: 12px; color: #999; }
</style>
Шаг 2: Создайте страницу сведений PostView
Страница сведений считывает ID из URL, затем находит и отображает соответствующую статью из данных статей.
Пример
<script setup>
import { ref, computed } from 'vue'
import { useRoute, RouterLink } from 'vue-router'
const route = useRoute()
const id = Number(route.params.id)
// Все данные статей (будут вынесены в общий модуль в следующих главах)
const articles = ref([
{
id: 1,
title: 'Полное руководство по началу работы с Vue3',
summary: 'Изучите Vue3 Composition API с нуля',
category: 'Vue',
date: '2024-05-10',
content: `
<h2>Почему стоит изучать Vue3?</h2>
<p>Vue3 — один из самых популярных фронтенд-фреймворков на сегодняшний день; он прост в изучении, гибок и показывает отличную производительность.</p>
<h2>Преимущества Composition API</h2>
<p>По сравнению с Options API во Vue2, Composition API делает повторное использование логики простым, а организацию кода — более гибкой.</p>
<h2>ref и reactive</h2>
<p>ref подходит для данных примитивных типов, а reactive — для объектов и массивов. Однако в реальной разработке рекомендуется постоянно использовать ref.</p>
`
},
{
id: 2,
title: 'Подробное объяснение асинхронного программирования на JS',
summary: 'Понимание Promise и async/await',
category: 'JavaScript',
date: '2024-05-08',
content: `
<h2>Что такое асинхронность?</h2>
<p>JS однопоточный; асинхронные операции позволяют главному потоку не блокироваться при выполнении таких задач, как сетевые запросы и таймеры.</p>
<h2>Основные концепции Promise</h2>
<p>Promise имеет три состояния: pending (в процессе), fulfilled (успешно), rejected (с ошибкой).</p>
`
}
])
// Найти статью по ID
const article = computed(() => {
return articles.value.find(a => a.id === id)
})
</script>
<template>
<div class="post-view">
<!-- Статья не найдена -->
<div v-if="!article" class="not-found">
<h2>Статья не найдена</h2>
<p>Статья с ID {{ id }} не найдена</p>
<RouterLink to="/">Назад на главную</RouterLink>
</div>
<!-- Статья существует, отобразить полное содержимое -->
<article v-else>
<span class="category-tag">{{ article.category }}</span>
<h1>{{ article.title }}</h1>
<time>{{ article.date }}</time>
<!-- v-html отображает HTML-строки -->
<div class="content" v-html="article.content"></div>
<RouterLink to="/" class="back-link">← Назад на главную</RouterLink>
</article>
</div>
</template>
<style scoped>
.post-view {
max-width: 720px;
margin: 40px auto;
padding: 0 20px;
}
.category-tag {
display: inline-block;
padding: 4px 12px;
background: #E8EEF5;
color: #427DB8;
border-radius: 12px;
font-size: 13px;
margin-bottom: 12px;
}
h1 {
font-size: 32px;
margin-bottom: 12px;
line-height: 1.4;
}
time {
display: block;
color: #999;
font-size: 14px;
margin-bottom: 30px;
}
.content {
line-height: 1.8;
font-size: 16px;
color: #333;
}
.content :deep(h2) {
margin: 24px 0 12px;
font-size: 22px;
}
.content :deep(p) {
margin-bottom: 12px;
}
.back-link {
display: inline-block;
margin-top: 40px;
color: #427DB8;
text-decoration: none;
}
.not-found {
text-align: center;
padding: 60px 0;
}
</style>
v-htmlиспользуется для рендеринга HTML-строк, но учтите: не используйте его для рендеринга пользовательского ввода, иначе возникает риск XSS-уязвимости. Содержимое статьи здесь написано самими разработчиками, поэтому это безопасно.
Краткое содержание главы
В этой главе вы изучили основы использования Vue Router: конфигурацию таблицы маршрутов (путь сопоставляется с компонентом), навигацию RouterLink, точку вывода RouterView, динамические маршруты /post/:id и useRoute для чтения параметров маршрута.
Теперь блог стал по-настоящему многостраничным — просматривайте список на главной странице, нажмите, чтобы перейти на страницу с подробностями и прочитать полное содержимое.
Другие расширения