☝ Предполагается, что к моменту прочтения этой статьи вы уже знаете, как создавать простое Vue-приложение. Также предполагается, что приложение открывается по адресу localhost:8080.

Для чего нужен маршрутизатор

При написании сайта или веб-приложения мы можем использовать обычные гиперссылки. Например, сделать панель навигации так:

<a href="/">Главная</a>
<a href="/settings">Настройки</a>
<a href="/contacts">Контакты</a>

Этот способ работает, но не всегда подходит, потому что каждый клик по ссылке приводит к загрузке новой страницы. Это нормальное поведение для статических сайтов — блога или ленты новостей. Но если мы хотим сделать интерфейс интерактивным, необходимо другое решение — например, использовать маршрутизатор.

Концепция Vue Router

Концепция маршрутизатора сводится к тому, что в зависимости от указанного пути и дополнительных параметров приложение отображает соответствующий контент. Например, наша логика подразумевает, что при переходе в /settings отобразится компонент с настройками, а в /contacts — форма обратной связи.

Давайте создадим простое Vue-приложение, которое реализует такую логику. Для быстрого старта воспользуйтесь шаблоном в CodeSandbox.

Мы намеренно опустим шаги по инициализации проекта, поскольку подразумевается, что с этим вы знакомы.

Для начала установим Vue Router:

npm install --save vue-router

Добавим в точку входа в приложение main.js следующий код:

import { createApp } from 'vue'
import { createRouter, createWebHistory } from 'vue-router'
import App from './App.vue'
import HomeView from './views/HomeView.vue'
const router = createRouter({
  routes: [{
    path: '/',
    component: HomeView
  }],
  history: createWebHistory()
})

const app = createApp(App)
app.use(router)
app.mount('#app')

Маршруты — это массив объектов, в котором мы описываем правила для отображения компонентов в зависимости от маршрута.

history — это настройка, которая отвечает за механизм поведения Vue Router. Мы указали createWebHistory(), то есть роутер будет использовать History API.

Чтобы посмотреть на работу Vue Router, добавим компонент <router-view>, который отвечает за отображение. Он является своего рода контейнером, в котором отрисовывается компонент соответствующего маршрута.

App.vue

<template>
  <router-view />
</template>

views/HomeView.vue

<template>
  <main>
    <h1>Главная</h1>
  </main>
</template>

Если мы откроем приложение в браузере, то увидим страницу, которую описали в HomeView.vue.

🚀 Изучите Vue.js 3

Научитесь с нуля создавать и стилизовать проект, добавлять анимации в веб-приложение и проводить тестирование.

Иерархия и вложенность маршрутов

Создадим страницу настроек views/SettingsView.vue:

<template>
  <main>
    <h1>Настройки</h1>
  </main>
</template>

Добавим новый объект в массив routes в файле main.js:

import SettingsView from './views/SettingsView.vue'

const router = createRouter({
  routes: [{
    path: '/',
    component: HomeView
  },
  {
    path: '/settings',
    component: SettingsView
  }],
  history: createWebHistory()
})

Откроем в браузере страницу [<http://localhost:8080/settings>](<http://localhost:4137/settings>) и увидим только что созданный нами компонент.

Теперь добавим дочерний маршрут для страницы настроек — например, настройки профиля:

views/SettingsProfileView.vue

<template>
  <main>
    <h2>Настройки профиля</h2>
  </main>
</template>

Укажем это в массиве routes в файле main.js:

import SettingsProfileView from './views/SettingsProfileView.vue'

const router = createRouter({
  routes: [{
    path: '/',
    component: HomeView
  },
  {
    path: '/settings',
    component: SettingsView,
    children: [{
      path: 'profile',
      component: SettingsProfileView
    }]
  }],
  history: createWebHistory()
})

Однако, открыв страницу по маршруту /settings/profile, мы не увидим изменений. Для отображения дочерних маршрутов их родитель (в нашем случае SettingsView.vue) должен также содержать <router-view>. Исправим это:

views/SettingsView.vue

<template>
  <main>
    <h1>Настройки</h1>
    <section>
      <router-view />
    </section>
  </main>
</template>

Теперь всё работает так, как мы и предполагали.

Навигация между маршрутами

Для привычной навигации по ссылкам необходимо использовать компонент <router-link>. Он принимает проп :to, который указывает, куда будет вести ссылка. Можно прописать полный путь или параметры маршрута — например, имя и query-парметры, которые мы рассмотрим далее.

Давайте зададим маршрутам имена и посмотрим, как это работает.

const router = createRouter({
  routes: [{
    path: '/',
    name: 'home',
    component: HomeView
  },
  {
    path: '/settings',
    name: 'settings',
    component: SettingsView,
    children: [{
      path: 'profile',
      name: 'settings-profile',
      component: SettingsProfileView
    }]
  }],
  history: createWebHistory()
})

В компоненте views/SettingsView.vue добавим ссылку на главную страницу:

<template>
  <main>
    <h1>Настройки</h1>
    <section>
      <router-view />
    </section>
    <footer>
      <router-link :to="{ name: 'home' }">
        На главную
      </router-link>
    </footer>
  </main>
</template>

Если мы откроем код страницы /settings в инспекторе, то увидим, что «На главную» — это обычный тег <a>. Но при нажатии на ссылку страница не станет перезагружаться — Vue Router меняет поведение ссылок, определённых при помощи <router-link>.

А если мы хотим реализовать навигацию на JavaScript — например, когда пользователь совершает какое-то действие, то можем воспользоваться методом push или replace. Они отличаются тем, что push сохраняется в истории навигации браузера, а replace перезаписывает текущее состояние.

Для примера добавим кнопку, открывающую настройки профиля, в компонент views/SettingsView.vue:

<template>
  <main>
    <h1>Настройки</h1>
    <section>
      <button @click="router.push({ name: 'settings-profile' })">
        Открыть настройки профиля
      </button>
    </section>
    <section>
      <router-view />
    </section>
    <footer>
      <router-link :to="{ name: 'home' }">
        На главную
      </router-link>
    </footer>
  </main>
</template>

<script setup>
  import {
    useRouter
  } from 'vue-router'
  const router = useRouter()
</script>

Теперь, находясь на странице /settings и нажав на кнопку, мы попадаем на /settings/profile и видим надпись «Настройки профиля».

Также добавим панель навигации на главную страницу. Пока что там будет только ссылка на настройки.

views/HomeView.vue

<template>
  <main>
    <h1>Главная</h1>
    <section>
      <ul>
        <li>
          <router-link :to="{ name: 'settings' }">
            Настройки
          </router-link>
        </li>
      </ul>
    </section>
  </main>
</template>

Маршрутизатор и Composition API

Vue Router предоставляет два основных Composition API для работы с маршрутами: useRouter и useRoute.

useRouter — используется для получения доступа к самому маршрутизатору. В примере выше мы вызывали useRouter(), чтобы получить объект маршрутизатора.

Прочитать про useRouter в документации

useRoute — используется для получения текущего маршрута. Рассмотрим его подробнее.

Ранее мы добавили кнопку «Показать настройки профиля», но она не пропадает, даже когда мы открываем эти настройки. Давайте исправим эту ошибку.

views/SettingsView.vue

<template>
  <main>
    <h1>Настройки</h1>
    <section v-if="route.name === 'settings'">
      <button @click="router.push({ name: 'settings-profile' })">
        Открыть настройки профиля
      </button>
    </section>
    <section>
      <router-view />
    </section>
    <footer>
      <router-link :to="{ name: 'home' }">
        На главную
      </router-link>
    </footer>
  </main>
</template>

<script setup>
  import {
    useRouter,
    useRoute
  } from 'vue-router'
  const router = useRouter()
  const route = useRoute()
</script>

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

Прочитать про useRoute в документации

Параметры маршрута

При помощи useRoute можно получить доступ к параметрам маршрута.

Создадим новый компонент views/UserView.vue:

<template>
  <main>
    <h1>Пользователь ID {{ id }}</h1>
    <section>
      <router-link :to="{ name: 'home' }">
        На главную
      </router-link>
    </section>
  </main>
</template>

<script setup>
  import {
    computed
  } from 'vue'
  import {
    useRoute
  } from 'vue-router'
  const route = useRoute()
  const id = computed(() => route.params.id)
</script>

Компонент предполагает, что текущий маршрут содержит параметр id. Давайте добавим этот идентификатор:

import UserView from './views/UserView.vue'

const router = createRouter({
  routes: [{
    path: '/',
    name: 'home',
    component: HomeView
  },
  {
    path: '/settings',
    name: 'settings',
    component: SettingsView,
    children: [{
      path: 'profile',
      name: 'settings-profile',
      component: SettingsProfileView
    }]
  },
  {
    path: '/user/:id',
    name: 'user',
    component: UserView
  }],
  history: createWebHistory()
})

Запись path вида /user/:id означает, что если мы зайдём на страницу /user/123, то маршрутизатор рассмотрит 123 как параметр id. Соответственно, если мы откроем /user/123 в браузере, то увидим текст «Пользователь ID: 123».

Добавим также ссылку на пользователя 123 на главной странице.

views/HomeView.vue

<template>
  <main>
    <h1>Главная</h1>
    <section>
      <ul>
        <li>
          <router-link :to="{ name: 'settings' }">
            Настройки
          </router-link>
        </li>
        <li>
          <router-link :to="{ name: 'user', params: { id: 123 } }">
            Пользователь 123
          </router-link>
        </li>
      </ul>
    </section>
  </main>
</template>

Прочитать про согласование маршрутов с параметрами

Страница ошибки «Страница не найдена»

Чтобы создать страницу для 404 ошибки, в конец массива маршрутов положим объект вида:

{
  path: '/:pathMatch(.*)*',
  name: '404',
  component: NotFoundView
}

main.js

import NotFoundView from './views/NotFoundView.vue'

const router = createRouter({
  routes: [{
      path: '/',
      name: 'home',
      component: HomeView
    },
    {
      path: '/settings'
      name: 'settings',
      component: SettingsView,
      children: [{
        path: 'profile',
        name: 'settings-profile',
        component: SettingsProfileView
      }]
    },
    {
      path: '/user/:id',
      name: 'user',
      component: UserView
    },
    {
      path: '/:pathMatch(.*)*',
      name: '404',
      component: NotFoundView
    }
  ],
  history: createWebHistory()
})

views/NotFoundView.vue

<template>
  <main>
    Страница не найдена
  </main>
</template>

Теперь, если мы зайдём на страницу по пути /whatever или любому несуществующему пути, то увидим надпись «Страница не найдена».

Прочитать про 404 в документации

Защищённые маршруты и метаданные

Предположим, нам нужно запретить переход неавторизованных пользователей по маршруту /settings. Обозначим дополнительное поле в объекте — метаданные:

const router = createRouter({
  routes: [{
    path: '/',
    name: 'home',
    component: HomeView
  },
  {
    path: '/settings'
    name: 'settings',
    component: SettingsView,
    children: [{
      path: 'profile',
      name: 'settings-profile',
      component: SettingsProfileView,
      meta: {
        requiresAuth: true
      }
    }]
  },
  {
    path: '/user/:id',
    name: 'user',
    component: UserView
  },
  {
    path: '/:pathMatch(.*)*',
    name: '404',
    component: NotFoundView
  }],
  history: createWebHistory()
})

Теперь мы сможем обращаться к этому полю, когда оперируем объектом текущего маршрута — например, как в примерах выше, с помощью useRoute().

Однако чтобы не делать проверку прямо в компонентах, воспользуемся встроенным механизмом Vue Router:

main.js

const router = createRouter({
  ...
})

// Для упрощения укажем это в переменной
const isAuthenticated = false

router.beforeEach((to, from) => {
  if (to.meta.requiresAuth && !isAuthenticated) {
    return {
      name: 'home'
    }
  }
})

В этом примере если неавторизованный пользователь попытается зайти на /settings/profile, выполнится редирект на главную.

Документация

Анимация переходов между страницами

☝ Предполагается, что к этому моменту вы уже знакомы с концепцией работы компонента <transition> во Vue.

Давайте добавим анимацию смены маршрутов в наше приложение. Откроем файл App.vue и изменим его <template>.

<template>
  <router-view v-slot="{ Component }">
    <transition name="fade" mode="out-in">
      <component :is="Component" />
    </transition>
  </router-view>
</template>

В данном примере мы получаем компонент, который рендерится внутри <router-view> (в нашем случае — корневого). Затем передаём этот компонент внутрь <transition>, который будет вызывать анимацию при смене компонента.

Если вы откроете приложение в браузере и попробуете перейти по страницам, ничего не произойдёт, ведь пока что у <transition> не добавлена анимация. Давайте это исправим. Добавим к <transition> атрибут name и укажем стили.

App.vue

<template>
  <router-view v-slot="{ Component }">
    <transition name="fade" mode="out-in">
      <component :is="Component" />
    </transition>
  </router-view>
</template>

<style>
  .fade-enter-active,
  .fade-leave-active {
    transition: opacity 0.5s ease;
  }

  .fade-enter-from,
  .fade-leave-to {
    opacity: 0;
  }
</style>

Теперь всё работает как нужно. Готовый проект в CodeSandbox.

Прочитать про анимации в документации

Заключение

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

Больше материалов


«Доктайп» — журнал о фронтенде. Читайте, слушайте и учитесь с нами.

ТелеграмПодкастБесплатные учебники

Читать дальше

9 книг по JavaScript для начинающих в 2024

9 книг по JavaScript для начинающих в 2024

🚀 Вам бесплатно доступен тренажёр по HTML и CSS.

Все вокруг говорят, что книги — прошлый век. Но вовремя прочитанная хорошая книжка может здорово помочь в изучении нового языка или технологии, а то и вообще целиком объяснить какую-нибудь важную штуку. Например, какие бывают алгоритмы, или зачем нужен рефакторинг. К тому же, хоть фреймворки меняются каждый год, основы обычно долго не меняются.

Мы опросили знакомых разработчиков, узнали, что читают они сами, и предлагаем вам подборку хороших книг по JavaScript.

Читать дальше
JS
  • 6 марта 2024
Объект URL в JavaScript: полный разбор

Объект URL в JavaScript: полный разбор

🚀 Вам бесплатно доступен тренажёр по HTML и CSS.

Объект URL в JavaScript представляет URL-адрес и предоставляет удобные методы для работы с ним. Он позволяет анализировать, конструировать и декодировать URL-адреса.

Создать объект URL можно двумя способами:

Конструктор URL() — самый распространённый способ, в котором вы передаёте любой URL в виде строки в качестве аргумента.

const url = new URL("https://www.example.com/path?query=123#hash");

Использование window.location — это глобальный объект в браузерах, который содержит информацию о текущем URL.

const currentUrl = new URL(window.location.href);
Читать дальше
JS
  • 23 января 2024
Генерация QR-кодов на JS в 4 шага. Node.js + qrcode

Генерация QR-кодов на JS в 4 шага. Node.js + qrcode

🚀 Вам бесплатно доступен тренажёр по HTML и CSS.

Давайте сделаем простой REST API на Node.js и Express, который будет генерировать QR-коды для любой ссылки. Если у вас ещё не установлены Node.js и npm, установите их с официального сайта.

Читать дальше
JS
  • 22 ноября 2023
Знакомство с JavaScript

Знакомство с JavaScript

🚀 Вам бесплатно доступен тренажёр по HTML и CSS.

Теперь, когда вы знаете, как создать структуру веб-страницы с помощью HTML и оформить ее стилями с помощью CSS, пришло время оживить её с помощью JavaScript (JS). JavaScript — это мощный язык программирования, который используется для создания интерактивных и динамических веб-сайтов.

Вы можете добавить JavaScript в ваш HTML-документ двумя способами:

Встроенный JavaScript: непосредственно в HTML-документ, в тегах <script>:

<script>
  alert("Привет, мир!");
</script>

Внешний JavaScript: подключение внешнего .js файла к HTML-документу:

<script src="script.js"></script>
Читать дальше
JS
  • 1 ноября 2023
Событие onclick в JS на примерах

Событие onclick в JS на примерах

🚀 Вам бесплатно доступен тренажёр по HTML и CSS.

Интерактивность — ключевой компонент любого современного сайта. И одним из наиболее часто используемых событий для создания интерактивности является событие onclick. В этой статье мы подробно разберёмся, что такое событие onclick, как его использовать и приведем примеры применения.

Событие onclick — это событие JavaScript, которое активируется, когда пользователь кликает на определенный элемент страницы. Это может быть кнопка, ссылка, изображение или любой другой элемент, на который можно нажать.

Читать дальше
JS
  • 30 октября 2023
Как перевернуть сайт. Самая короткая инструкция

Как перевернуть сайт. Самая короткая инструкция

Не представляем, зачем это может понадобиться, но не могли пройти мимо.

Никакой магии. Мы вызываем JavaScript-функцию rotateBody(), которая применяет свойство transform с значением rotate(180deg) к элементу <body>. Когда вы нажмете на кнопку «Перевернуть», всё, что находится внутри <body> будет повернуто на 180 градусов (то есть, встанет вниз головой)

function rotateBody() {
  document.body.style.transform = 'rotate(180deg)';
}

<button onclick="rotateBody()">Перевернуть</button>

Но такой код повернёт страницу только один раз. Если нужно, чтобы она возвращалась обратно при втором клике, усложним код:

let isRotated = false;

function rotateBody() {
  if (isRotated) {
    document.body.style.transform = 'rotate(0deg)';
    document.body.style.direction = "ltr";
  } else {
    document.body.style.transform = 'rotate(180deg)';
    document.body.style.direction = "rtl";
  }
  isRotated = !isRotated;
}

Надеемся, вы прочитали это описание до того, как нажать на кнопку.

JS
  • 25 октября 2023
Как узнать геолокацию: Geolocation API

Как узнать геолокацию: Geolocation API

🚀 Вам бесплатно доступен тренажёр по HTML и CSS.

Geolocation API позволяет сайтам запрашивать, а пользователям предоставлять свое местоположение веб-приложениям. Геолокация может использоваться для выбора города в интернет-магазине, отображения пользователя на карте или навигации в ближайший гипермаркет.

Основной метод Geolocation API — getCurrentPosition(), но есть и другие методы и свойства, которые могут пригодиться.

Читать дальше
JS
  • 16 октября 2023
Что такое localStorage и как им пользоваться

Что такое localStorage и как им пользоваться

🚀 Вам бесплатно доступен тренажёр по HTML и CSS.

localStorage — это место в браузере пользователя, в котором сайты могут сохранять разные данные. Это как ящик для хранения вещей, которые не исчезнут, даже если вы выключите компьютер или закроете браузер.

До localStorage разработчики часто использовали cookies, но они были не очень удобны: мало места и постоянная передача данных туда-сюда. LocalStorage появился, чтобы сделать процесс более простым и эффективным.

Читать дальше
JS
  • 12 октября 2023