Навигация по маршрутам

В этой главе вы научитесь использовать Vue Router для реализации навигации по страницам с разделением фронтенда и бэкенда, включая динамические параметры маршрута.


Зачем использовать маршрутизацию?

Традиционные многостраничные сайты запрашивают у сервера новый HTML-файл при клике по ссылке, вызывая полную перезагрузку страницы с мерцанием белого экрана.

Vue Router реализуетфронтенд-маршрутизацию: при изменении URL запрос на сервер не отправляется; вместо этого JS динамически переключает отображаемый компонент.

Преимущества такого подхода:Переключение страниц без перезагрузки, обеспечивающее плавную работу, как в нативном приложении。


Основные концепции Vue Router

КонцепцияНазначениеСоответствующий код
Таблица маршрутовОпределяет сопоставление между путями URL и компонентамиroutesМассив
Точка вывода маршрутизатораМесто, где отображается совпавший компонент<RouterView />
Навигационная ссылкаНажмите, чтобы перейти к указанному маршруту<RouterLink to="...">
Динамическая маршрутизацияURL содержит переменные (например, ID статьи)/post/:id

Установка и настройка

Если вы уже выбрали Router при создании проекта, можете пропустить этап установки.

Если вы его не выбрали, выполните в терминале:

$ npm install vue-router@4

Создайте файл конфигурации маршрутов

Пример

// Путь к файлу: src/router/index.js
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

Пример

<!-- Путь к файлу: src/App.vue -->
<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.

Пример

<template>
  <!-- Обычный синтаксис -->
  <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используется для чтения информации о текущем маршруте.

Пример

<!-- Путь к файлу: src/views/PostView.vue -->
<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 для поддержки навигации по клику

Пример

<!-- Путь к файлу: src/components/BlogCard.vue -->
<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, затем находит и отображает соответствующую статью из данных статей.

Пример

<!-- Путь к файлу: src/views/PostView.vue -->
<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 для чтения параметров маршрута.

Теперь блог стал по-настоящему многостраничным — просматривайте список на главной странице, нажмите, чтобы перейти на страницу с подробностями и прочитать полное содержимое.

Другие расширения