Роутинг в Vue
Практика использования официальной библиотеки для маршрутизации в Vue — Vue router.
50К открытий53К показов
Практика использования официальной библиотеки для маршрутизации в Vue — Vue router. О том, как настраивать маршрутизацию в React Router, рассказали здесь.
Константин Базров
ведущий разработчик NGRSOFTLAB
С появлением веб-приложений пришла потребность в смене URL-адресов с помощью JS. На помощь пришел History API браузера.
Благодаря этому все основные современные фреймворки позволяют программно управлять маршрутизацией с синхронизацией URL-адреса с представлением приложения.
Для маршрутизации во Vue-приложениях можно создать свою собственную интеграцию с History API, но лучше использовать официальную библиотеку от Vue — Vue-Router.
Базовые вещи
Использование можно начать хоть с установки с CDN:
<script src="https://unpkg.com/vue-router"></script>
Но мы начнем сразу с «правильного» варианта — с Vue Cli:
Создадим проект с помощью VUE CLI с базовым шаблоном — Default ([Vue 2] babel, eslint):
vue create vue-router-test-app
Минимальная конфигурация
Добавим роутер:
Добавим в Main.js минимальную конфигурацию для роутера:
/src/main.js
Роуты представляют собой массив, каждый элемент которого — объект, где требуется указать path
и component
.
Чтобы увидеть изменения надо вывести компонент роутера — routerView
, который отвечает за отображение. Для этого изменим App.vue:
/src/App.vue
Теперь, зайдем на http://localhost:8080/. Увидим страницу с маршрутом «/», где отображается компонент HelloWorld.vue, вместо тега router-view
, который мы писали в App.vue.
Иерархия путей
Добавим маршрут в main.js (массив routes
):
Зайдем по адресу http://localhost:8080/board. Увидим вторую страницу с отображением рендер-функции.
Параметры (Props) маршрута
Поправим дочерний маршрут для маршрута /board в main.js. Для дочерних компонентов надо указывать где в родительском компоненте отображать дочерние — компонентом router-view
. В нашем случае — это в рендер-функция:
Напомню, что рендер-функция в template-представлении будет выглядеть следующим образом:
Создадим компонент Board.vue с содержимым:
/src/components/Board.vue
Перейдем по адресу http://localhost:8080/board/21 и увидим родительский и дочерний компоненты Board
с передачей параметра id
равным 21.
Параметры маршрута доступны в компоненте по this.$route.params
.
Если хотим более явно отобразить зависимость компонента от входных параметров, используем настройку props: true
при настройке маршрута:
/src/main.js
А в компоненте Board.vue принять id
как входной параметр компонента:
/src/components/Board.vue
Метаданные (meta) маршрута
/src/main.js
Теперь мы можем обратиться к метаданным роута из компонента HelloWorld.vue следующим образом:
this.$route.meta.dataInMeta
.
Глубже (nested children)
В дочерние компоненты можно углубляться до бесконечности (до ограничений сервера).
Сделаем дочерний роут для дочернего роута.
/src/main.js
Рендер-функция теперь записана обычной функцией, т.к. нужен контекст компонента.
/src/components/Board.vue
Передаем дочернему компоненту дочернего компонента параметры через компонент router-view как обычному компоненту. Звучит сложно, но интуитивно понятно. И так, спускаем пропсы в дочернем — дочернему дочернего:
<router-view :prop-to-child="parseInt(id)" />
Пояснение за Path
Запись вида path: "child"
означает, что мы обращаемся к пути родителя и продолжаем его путь: {parent-route}/child
Из дочернего компонента можно сослаться на любой другой уровень роута:
/src/main.js (routes):
Эта запись обрабатывает страницу с адресом: http://localhost:8080/first-level.
Шире (несколько router-view)
Можно использовать несколько router-view
в 1 компоненте. Для этого в конфигурации маршрутов (routes) пишем вместо component — components, который принимает объект, где ключ — атрибут name
у router-view
. Если указать ключ «default», то такой компонент будет отображаться, если router-view
безымянный (без атрибута name
).
/src/main.js
/components/Board.vue
Перейдем по адресу: http://localhost:8080/board/23/child и увидим небольшой интерактив с переключением активных router-view.
Страница ошибки 404
Чтобы создать страницу ошибки, достаточно положить в конец списка маршрутов такую конструкцию:
/src/main.js(routes)
Теперь, при переходе по несуществующему пути (например — http://localhost:8080/mistake), будет выведен компонент ошибки.
Лучше писать в таком виде:
/src/main.js
Теперь у нас есть страница с ошибкой, куда мы можем со спокойной совестью переадресовывать пользователей (вдруг когда-нибудь понадобится это делать).
Защита маршрутов
Защиту маршрутов осуществляют с использованием метаданных маршрутов и хука beforeEach
роутера.
/src/main.js
Теперь, при попытке получить доступ к странице, которая требует авторизации, нас перебросит на страницу /auth-required.
Навигация между маршрутами
Программная навигация
Программная навигация может вызываться из любого места вашего приложения таким образом:
$router.push('/dash/23/child')
Если мы хотим передать параметры, нам нужно использовать другой подход, основанный на использовании имен роутов.
Укажем имя роуту /board/:id
:
Теперь мы можем передавать параметры:
$router.push({ name: 'board', params: { id: 100500 }})
Получим ошибку «Invalid prop: type check failed for prop “id”. Expected String with value “100500”, got Number with value 100500».
Причина в том, что url
— это всегда тип данных String
, а мы передали программно id
с типом Number
. Исправляется это просто: перечислим возможные типы данных в компоненте.
components/Board.vue
Компонент routerLink
Компонент routerLink
позволяет создавать ссылки внутри сайта, которые преобразуются в «нативные» браузерные ссылки (тег <а>
):
<router-link to='/dash/23/child'> Link </router-link>
К таким ссылкам автоматически могут добавляться классы:
router-link-exact-active
— точное совпадение;router-link-active
— частичное (активен дочерний компонент указанного в атрибутеto
роута).
Чтобы не отображать активный класс родительских, достаточно написать атрибут exact
:
<router-link to='/dash/23/child' exact> Link </router-link>
Мы можем переопределить создаваемый элемент:
<router-link tag="button" to='/dash'> Button </router-link>
К сожалению, в таком случае, классы не проставляются.
Также можем передавать объект:
<router-link :to="{ path: '/dash/23' "> Link </router-link>
<router-link :to="{ name: 'board', params: { id: 123 } }"> Link </router-link>
Лучшие практики
Этот раздел мы посвятим рефакторингу того, что мы написали выше.
Создаем структуру папок для роутера:
Перенесем в router.js все, что касается настроек роутера:
Перенесем routes.js все, что касается настроек маршрутов.
И сразу заменим импорты на динамические.
Если у Вас уже прописано много роутов, ручное изменение может потребовать много времени. Поможет регулярка:
^import (\w+) from (".+")$
заменить на
const $1 = () => import(/* webpackChunkName: "$1" */ $2)
Теперь в Chrome Dev Tools во вкладке Network будет видно когда-какой компонент грузится из сети, а раньше все роуты загружались сразу в 1 мега-бандле.
src/router/routes.js
Продвинутые приемы
Под «продвинутостью» подразумевается «приятность» их использования. К таким приемам можно отнести, например, такие темы как:
- разбиение прав по уровням доступа;
- анимацию переходов между страницами;
- индикацию загрузки при переходе между роутами;
- изменение тайтлов при переходе между роутами;
- плавный скролл по странице при переходе назад;
- и т.п.
Итак, обо всем по-порядку.
Разбиение прав по уровням доступа
Бывает ситуация, когда у пользователей бывает более двух состояний: не только авторизация, но и другие. Например, платная подписка. С этих пор мы задумываемся про неограниченный уровень разделения прав. Делается это буквально парой десятков строчек кода, но для краткости, удобства и чтобы не изобретать велосипед, мы будем использовать готовую библиотеку. Установим ее:
yarn add vue-router-middleware-plugin
Создадим специальные файлы middleware для проверки прав пользователей:
router/middleware/authMiddleware.js
router/middleware/guestMiddleware.js
router/middleware/subscribersMiddleware.js
В последнем листинге, приведен пример асинхронной проверки, что значит — можно обращаться в actions стора и делать запросы на сервер.
Теперь поставим проверку на авторизацию на все роуты, а затем сделаем исключения для некоторых роутов:
/src/router/router.js
Теперь разберемся с конкретными маршрутами.
Поработаем над архитектурой нашего приложения, чтобы сделать его более предсказуемым. Сделаем отдельный шаблон Auth.vue и положим его в pages, а компоненты, которые там используются, т.е. в разделе /auth, положим в соответствующий раздел components.
Т.о. получается удобная структура:
Создадим вспомогательную функцию для генерации подобных роутов genAuthRoutes
.
/src/router/routes.js
Удаляем глобальную проверку на авторизацию в свойстве ignore
и добавляем другую проверку в свойстве attach
объекта meta.middleware
:
Создадим компоненты
- src/components/auth/Login.vue;
- src/components/auth/Register.vue;
- src/components/auth/Forgot.vue,
с типовым шаблоном:
Также отрефакторим страницу Board
, назовем его MainBoard
/src/pages/MainBoard.vue
Соответственно, добавляем компоненты в соответствующую категорию в components:
/src/components/board/BoardComponent.vue
Осталось отрефакторить главный компонент — App.vue:
/src/App.vue
Теперь, снимем отметку с «Logged In» и попробуем перейти по маршруту http://localhost:8080/board. Нас незамедлительно переадресует на страницу «auth-required».
Поставим отметку на «Logged In», снимем с «Has License» и перейдем по маршруту http://localhost:8080/board/33/child. Нас перенесет на страницу license, однако, если снять отметку с «Logged In» и обновить страницу, то мы снова перейдем на страницу «auth-required».
Теперь проверим, можно ли зайти на страницу авторизации, когда пользователь уже прошел авторизацию. Поставим отметку «Logged In» и перейдем по адресу http://localhost:8080/auth/register. Нас перебросит на главную страницу.
Анимация переходов между страницами
Это просто. Оборачиваем главный RouterView
компонентом анимации transition
и добавляем стили:
src/App.vue
Индикация загрузки при переходе между роутами
Это тоже просто. Ставим библиотеку nprogress:
yarn add nprogress
Добавляем в router.js:
/src/router/router.js
Изменение тайтлов при переходе между роутами
И это тоже просто.
Заполняем meta.title
маршрутам и ставим document.title
каждой странице в хуке beforeEach
:
/src/router/router.js
Плавный скролл по странице при переходе вперед/назад
Когда жмешь по браузерным «системным» кнопкам назад или вперед, браузер запоминает положение прокрутки и возвращает. Такое поведение мы можем повторить.
/src/router/router.js
50К открытий53К показов