Маршрутизация на основе файлов в Vue.js
Управление конфигурациями маршрутов в приложениях Vue.js может быстро стать утомительным и отнимать много времени, особенно по мере роста и усложнения вашего приложения. Это связано с тем, что оно содержит много повторяющегося кода, что затрудняет навигацию по базе кода. Кроме того, вам необходимо постоянно обновлять файл routes.js всякий раз, когда вы создаете, редактируете или удаляете маршруты, что может привести к ошибкам и неправильным маршрутам.
Один из способов избежать этих проблем - использовать маршрутизацию на основе файлов. Это система, в которой маршруты в вашем приложении автоматически определяются файлами и папками в вашем проекте, а не настраиваются вручную в файле конфигурации. К сожалению, эта функция в основном доступна в мета-фреймворках, таких как Nuxt.
Однако существуют плагины Vue Router, которые предоставляют функции, схожие с Vue.js. Одним из таких плагинов является unplugin-vue-router, который, помимо прочих преимуществ, обеспечивает автоматическую генерацию маршрутов и упрощенную навигацию.
В этой статье я покажу вам, как работает автоматическая маршрутизация на основе файлов, как настроить ее в приложении Vue.js и чем она отличается от традиционных методов маршрутизации.
Как работает автоматическая маршрутизация на основе файлов в Vue
Идея автоматической маршрутизации на основе файлов проще, чем кажется. Система работает, просматривая в определенной папке (часто называемой каталогом маршрутов) файлы с определенными расширениями, например .vue, а затем автоматически создает соответствующие маршруты на основе имен файлов, игнорируя часть .vue. Например:
http://localhost:5173/about
Это означает, что все, что вам нужно сделать, это добавить компонент .vue в папку, и система выполнит остальную маршрутизацию за вас.
Чтобы сделать это более понятным, давайте представим проект с определенной структурой папок, где папка pages служит каталогом routes, что распространено во многих современных фреймворках:
src/pages/
├── index.vue
├── about.vue
└── users/
└── index.vue
Маршруты создаются для каждого файла с расширением .vue и для любой папки, содержащей файл index.vue. Файл index.vue является содержимым по умолчанию для этого маршрута, то есть это то, что пользователи будут видеть при посещении маршрута.
Например, файл index.vue в папке main pages будет привязан к корневому пути (/), также известному как “Hompage”, в то время как файл about.vue будет привязан к пути /about, и то же самое относится к пути /user.
Приступая к работе
Чтобы продемонстрировать, как работает автоматическая маршрутизация на основе файлов, нам понадобится проект Vue. Если у вас его нет, вы можете создать новое приложение Vue, выполнив следующую команду в программе командной строки вашего компьютера.:
npm create vue@latest
Команда предложит вам выбрать ваши предпочтения для приложения, и следующего должно быть достаточно:
✔ Project name: … <vue-project>
✔ Add TypeScript? … No / Yes
✔ Add JSX Support? … No / Yes
✔ Add Vue Router for Single Page Application development? … No / Yes
✔ Add Pinia for state management? … No / Yes
✔ Add Vitest for Unit testing? … No / Yes
✔ Add an End-to-End Testing Solution? … No / Cypress / Nightwatch / Playwri..
✔ Add ESLint for code quality? … No / Yes
✔ Add Prettier for code formatting? … No / Yes
✔ Add Vue DevTools 7 extension for debugging? (experimental) … No / Yes
После успешной установки приложения перейдите в каталог проекта и установите необходимые зависимости, используя следующую команду:
npm i && npm i -D unplugin-vue-router
Эта команда установит все необходимое для приложения Vue, включая библиотеку unplugin-vue-router. После завершения установки следующим шагом будет настройка unplugin-vue-router в нашем проекте.
Настройка unplugin-vue-router
Прежде чем мы сможем начать использовать автоматическую маршрутизацию на основе файлов в нашем приложении, нам сначала нужно подключить unplugin-vue-router к системе маршрутизации приложения и настроить структуру проекта в соответствии с этим методом маршрутизации.
В качестве первого шага откройте файл vite.config.ts в корневой папке вашего проекта и добавьте плагин в конфигурацию вашего пакета:
import VueRouter from 'unplugin-vue-router/vite'
export default defineConfig({
plugins: [
VueRouter({
/* options */
}),
Vue(),
],
})
Убедитесь, что VueRouter находится с индексом 0, то есть перед Vue.
Далее мы обновим некоторые конфигурации, чтобы обеспечить корректную работу автозаполнения и обнаружения ошибок в нашем приложении. Для этого откройте файл tsconfig.json в основной папке вашего проекта и добавьте следующий код:
{"include": [// other files..."./typed-router.d.ts"],"compilerOptions": {// ..."moduleResolution": "Bundler","types": ["unplugin-vue-router/client"]// ...},"references": [{"path": "./tsconfig.node.json"},{"path": "./tsconfig.app.json"}]
}
Файл typed-router.d.ts помогает unplugin отслеживать все маршруты и тропинки, позволяя TypeScript предлагать автоматическое завершение и корректно обрабатывать импорт модулей.
Обратите внимание, что конфигурационный файл ожидает, что в корневом каталоге будет указан файл-router.d.ts. Обычно он создается автоматически при запуске сервера разработки после добавления VueRouter в файл конфигурации. Если вы не видите его в корневой папке, попробуйте запустить сервер разработки, чтобы сгенерировать его.
После этого обновите файл env.d.ts, чтобы убедиться, что ваша среда разработки находит необходимые файлы для вашего проекта:
/// <reference types="vite/client" />
/// <reference types="unplugin-vue-router/client" />
Далее мы переопределим маршрутизатор по умолчанию. Для этого перейдите в src/router и обновите файл index.vue следующим кодом:
import { createRouter, createWebHistory } from 'vue-router'
import { routes } from 'vue-router/auto-routes'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes
})
export default router
Здесь вместо того, чтобы вручную создавать массив маршрутов и определять маршруты, unplugin выполняет это автоматически и передает его непосредственно экземпляру маршрутизатора.
Наконец, переименуйте view
папку в pages
и измените файлы AboutView.vue
и HomeView.vue
внутри нее на about.vue<strong>
и </strong>index.vue<strong>
соответственно.
Вот и все! Вы успешно добавили unplugin
в свой проект и настроили автоматическую маршрутизацию на основе файлов. Если все настроено правильно, вы сможете перейти на любую страницу в pages
каталоге. Посмотрите на GIF ниже, чтобы увидеть это в действии:
Перенос существующих проектов
При переносе существующего приложения для использования автоматической маршрутизации на основе файлов вам в значительной степени необходимо выполнить шаги, описанные в предыдущих разделах, а именно: настроить конфигурацию маршрутизатора для использования автоматически генерируемых маршрутов, предоставляемых unplug-vue-router, определить файловую структуру в каталоге pages и добавить необходимые определения типов с использованием файла typed-router.d.ts.
Вам также может потребоваться обновить любой код, который зависит от определенных маршрутов. Не волнуйтесь, вам не нужно будет менять способ использования RouterView и routerLink для отображения страниц и навигации между ними; они будут продолжать работать так же, как и раньше:
<nav><RouterLink to="/">Home</RouterLink><RouterLink to="/about">About</RouterLink><RouterLink to="/blog">Blog</RouterLink></nav>
<RouterView />
Однако для некоторых функций, таких как маршруты, которые изменяются в зависимости от пользовательского ввода (динамические маршруты) или маршруты внутри других маршрутов (вложенные маршруты), вам потребуется использовать другие методы.
Динамические маршруты
В отличие от традиционных методов маршрутизации, создание динамических маршрутов в файловых системах осуществляется в соответствии с определенными соглашениями. Например, вы можете использовать специальные имена файлов, такие как [id].vue или [slug].vue, для создания динамических частей URL, таких как blog/123 или store/123. Соответствующий компонент будет отображен на основе значения в URL-адресе.
Файловая структура будет выглядеть следующим образом:
src/
├── pages/
├── users/
│ └── [id].vue // Maps to to '/users/:id'
├── blog/
│ └── [slug].vue // Maps to '/blog/:slug'
└── index.vue // Maps to '/'
Хотя это происходит незаметно, unplugin-vue-router сгенерирует конфигурацию маршрута для вас на основе динамических маршрутов из файловой структуры, приведенной выше:
const routes = [
{ path: '/users/:id', component: () => import('./pages/users/[id].vue') },
{ path: '/blog/:slug', component: () => import('./pages/blog/[slug].vue') },
{ path: '/', component: () => import('./pages/index.vue') },
];
Динамическая маршрутизация на основе данных
Лучший способ продемонстрировать динамическую маршрутизацию - объединить ее с внешними данными и динамически генерировать маршруты на основе полученной информации. Мы создадим маршрут /store в нашем примере приложения и будем динамически генерировать маршруты для продуктов на основе данных, полученных из API.
Начните с добавления маршрута /store в ваше приложение. После внесения этого обновления структура вашего файла должна выглядеть следующим образом:
src/
├── pages/
├── /* Other routes */
├── store/
│ ├── index.vue
│ └── [id].vue
Затем добавьте следующий код в соответствующий файл index.vue:
import { ref, onMounted } from 'vue'
import type { IProduct } from '@/types'
export default {
setup() {
const products = ref<IProduct[]>([])
const regex = /[^a-z0-9A-Z-://.]/g
const fetchProduct = async () => {
try {
const res = await fetch('https://api.escuelajs.co/api/v1/products')
if (!res.ok) throw new Error('failed to fetch posts')
products.value = await res.json()
} catch (error) {
console.log(error)
}
}
onMounted(() => {
fetchPosts()
})
return {
products,
regex
}
}
}
</script>
<template><div class="about"><h1>/Store</h1><div><ul><li v-for="item in products" :key="item.id"><router-link :to="`/store/${item.id}`"><div class="card"><img class="card_image" :src="item.images[0].replace(regex, '')" :alt="item.title" /><div class="info"><p class="title">{{ item.title.substring(0, 25) + '...' }}</p><p class="price">${{ item.price }}</p></div></div></router-link></li></ul></div></div>
</template>
<style>
@media (min-width: 1024px) {
.about {
min-height: 100vh;
align-items: center;
}
li {
list-style-type: none;
}
.card_image {
width: 80px;
margin-right: 10px;
}
.card {
display: flex;
align-items: flex-start;
border: #00bd7e solid 2px;
padding: 20px;
}
.title {
font-size: 20px;
color: white;
}
.info {
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: space-between;
height: 80px;
}
.price {
font-size: 20px;
color: white;
background-color: #00bd7e;
padding: 2px 10px;
border-radius: 10px;
}
}
</style>
Вот что происходит: мы используем функцию под названием fetchProducts для получения данных о продукте при первой загрузке компонента. Это делается с помощью функции onMounted, поэтому данные готовы при монтаже компонента и сохраняются в массиве products.
В структуре шаблона мы перебираем массив продуктов и отображаем каждый элемент в виде списка: li. Компонент router-link создает динамические ссылки на страницу сведений о каждом продукте, используя его идентификатор.
Ваш маршрут /магазин должен выглядеть примерно так, как на этом GIF-изображении:
При нажатии на карточку товара идентификатор в URL (динамический сегмент) заменяется на фактический идентификатор товара. Затем этот идентификатор становится доступным в объекте параметров маршрута.
Теперь, когда у нас есть доступ к идентификатору продукта, мы можем получать и отображать информацию об этом продукте на новой странице (динамический маршрут). Чтобы настроить это, откройте файл [id].vue и добавьте следующий код:
<script lang="ts">
import { onMounted, ref } from 'vue'
import { useRoute } from 'vue-router/auto'
import type { IItem } from '@/types'
export default {
setup() {
const route = useRoute()
const item = ref<IItem>()
const regex = /[^a-z0-9A-Z-://.]/g
const fetchItem = async (id: any) => {
try {
const res = await fetch(`https://api.escuelajs.co/api/v1/products/${id}`)
if (!res.ok) throw new Error('Failed to fetch post')
item.value = await res.json()
} catch (error) {
console.log('Error:' + error)
}
}
onMounted(() => {
const { id } = route.paramsfetchItem(id)
})
return {
item,
regex
}
}
}
</script>
<template><div v-if="item"><h1>{{ item.title }}</h1><p>{{ item.description }}</p><div class="img"><img :src="item.images[0].replace(regex, '')" :alt="item.title" /></div></div><div v-else><p>...Loading</p></div>
</template>
<style>
p {
margin-bottom: 15px;
place-items: center;
}
img {
width: 100%;
}
.img {
width: 400px;
margin-left: auto;
margin-right: auto;
}
</style>
В этом коде мы, по сути, выполняем тот же процесс, что и раньше, но теперь мы динамически добавляем идентификатор продукта для получения его конкретных данных. При такой настройке динамические маршруты в вашем приложении теперь должны работать должным образом.
Вложенные маршруты
Вложенные маршруты в файловой системе маршрутизации просты, поскольку вложенные маршруты автоматически генерируются на основе структуры подкаталогов в папке pages. Например, файл [id].vue в предыдущем примере представляет собой вложенный маршрут и может быть переработан следующим образом:
src/
├── pages/
├── /* Other routes */
├── store/
│ ├── index.vue
│ └── [id]/
│ └── index.vue
Unplugin упрощает создание вложенных маршрутов, что видно из приведенного выше примера. Все, что вам нужно сделать, это создать необходимые подкаталоги для определения вложенных маршрутов, а плагин позаботится об остальном за вас.
Этот подход значительно более прост по сравнению с традиционным методом использования конфигурации маршрута с помощью Vue Router, который, как показано в приведенном ниже отрывке, может быстро стать неоправданно сложным и трудноуправляемым:
{
path: 'store',
component: () => import('../pages/store/index.vue'),
children: [
{
path: ':id',
component: () => import('../pages/store/[id].vue'),
},
}
Вложенные динамические маршруты
На мой взгляд, система маршрутизации на основе файлов по-настоящему эффективна при работе с вложенными динамическими маршрутами. Это динамические маршруты, которые включают вложенные пути, отображаемые динамически рядом с родительским динамическим маршрутом.
Например, если мы хотим динамически генерировать маршрут /settings для каждого товара в нашем магазине, нам просто нужно поместить файл settings.vue в каталог [id]/ рядом с файлом index.vue:
src/
├── pages/
├── /* Other routes */
├── store/
│ ├── index.vue
│ └── [id]/
│ ├── index.vue
│ └── settings.vue
Сравните это с обычным методом, который потребовал бы от вас создания конфигурации маршрута, подобной приведенной ниже:
постоянные маршруты = [
{
path: '/store',
component: () => import('@/pages/store/index.vue'),
children: [
{
path: ':id',
component: () => import('@/pages/store/[id]/index.vue'),
children: [
{
path: 'settings',
component: () => import('@/pages/store/[id]/settings.vue'),
},
],
},
],
},
]
Вложенные маршруты без вложенных макетов
Если вложение файлов маршрутов в родительский маршрут не подходит для вашего варианта использования, unplugin предлагает альтернативный метод создания вложенных маршрутов без использования структуры каталогов "родитель-потомок".
Вы можете сделать это, добавив имя родительского маршрута к имени файла дочернего маршрута. Например, чтобы создать маршрут /store/categories, вместо размещения файла categories.vue в каталоге store, выполните следующее:
src/
├── pages/
├── /* Other routes */
├── store/
│ ├── index.vue
│ ├── [id].vue
│ └── categories.vue
Вы можете просто добавить его в корневой каталог pages и добавить store. к имени файла:
src/
├── pages/
├── /* Other routes */
├── store/
│ ├── index.vue
│ └── [id].vue
└── store.categories.vue
Эта файловая структура будет сопоставлена с маршрутом /pages/store/categories. . между именами родительского и дочернего маршрутов будет автоматически преобразовано в / при создании маршрутов:
const routes = [
{
path: '/store',
component: () => import('@/pages/store/index.vue'),
children: [
{
path: ':id',
component: () => import('@/pages/store/[id]/index.vue'),
}
],
},
{
path: '/store/categories',
component: () => import('@/pages/store.categories.vue'),
},
]
Catch-All […all] route
catch-all маршрут - это важный путь, используемый для обработки запросов, которые не соответствуют никаким другим маршрутам в сгенерированной конфигурации маршрута. Представьте, что пользователь пытается получить доступ к несуществующему маршруту, например, к страницам/блогу в нашем примере приложения; он столкнется с пустой страницей, что может привести к путанице.
Однако с помощью универсального маршрута мы можем перенаправлять пользователей на страницу 404, сообщая им, что они пытаются получить доступ к несуществующему маршруту.
Традиционно универсальные маршруты определяются с помощью route path, такого как /* или *, и обычно размещаются последними в массиве конфигурации маршрутов, поскольку маршрутизатор Vue сопоставляет маршруты в том порядке, в котором они определены:
const routes = [
{
path: '/',
name: 'Home',
component: Home,
},
{
path: '/about',
name: 'About',
component: About,
},
// Catch-all route (404 page)
{
path: '/:pathMatch(.*)*', // This pattern matches all undefined paths
name: 'NotFound',
component: NotFound,
},
];
Однако при маршрутизации на основе файлов нам не обязательно придерживаться этих правил, и создать универсальный маршрут так же просто, как создать файл с тремя точками перед именем параметра, например [...path].vue:
src/
├── pages/
├── /* Other routes */
├── [...all].vue
├── store/
│ ├── index.vue
│ └── [id].vue
Это будет соответствовать любому несуществующему маршруту.
Обратите внимание, что это также можно сделать внутри папок для обработки несуществующей вложенной навигации по маршрутам, например, pages/store/[...path].vue:
src/
├── pages/
├── /* Other routes */
├── [...all].vue
├── store/
│ ├── index.vue
│ ├── [id].vue
│ └── [...all].vue
Расширенные конфигурации с функцией VueRouter
До сих пор мы использовали конфигурацию unplugin по умолчанию. Однако плагин также позволяет выполнять более сложные настройки с помощью функции VueRouter, которую мы ранее использовали для создания экземпляра маршрутизатора в пакете нашего приложения:
import VueRouter from 'unplugin-vue-router/vite'
export default defineConfig({
plugins: [
VueRouter({
/* options */
}),
...
],
})
Функция принимает параметр object, который мы можем использовать для определения различных расширенных конфигураций. К ним относятся следующие.
Пользовательская папка маршрута
Unplugin-vue-router позволяет вам указывать пользовательский каталог для ваших маршрутов вместо использования каталога src/pages по умолчанию. Эта функция особенно полезна, если вы хотите организовать свои маршруты в отдельные модули или пространства имен.
Чтобы настроить это, определите параметр routesFolder в объекте VueRouter и передайте путь к вашей пользовательской папке маршрутов в виде строки:
export default defineConfig({
plugins: [
VueRouter({
routesFolder: 'src/views'
}),
...
],
})
Это изменение установит каталог src/views в качестве новой папки маршрута, что означает, что все файлы маршрутов в этой папке будут отображаться вместо файлов в каталоге src/pages по умолчанию.
Несколько папок маршрутов
Unplugin также позволяет вам определить несколько папок маршрутов. Это можно сделать, передав массив из нескольких строк путей в параметр routesFolder вместо одного пути, как мы делали в предыдущем разделе:
export default defineConfig({
plugins: [
VueRouter({
routesFolder: ['src/views', 'src/views']
}),
...
],
})
При такой настройке каталоги views и pages будут использоваться в качестве папок маршрутов, что позволит вам управлять маршрутами из нескольких мест в вашем проекте.
Это может быть особенно полезно для крупномасштабных приложений и когда вы хотите разделить задачи внутри вашего приложения, например, использовать разные структуры маршрутов для пользовательских страниц, панелей администратора и разделов блога.
Пользовательские расширения
Еще одной расширенной функцией плагина являются пользовательские расширения. Это позволяет вам указывать пользовательские расширения, которые вы хотите использовать как страницы. Например, если вы хотите использовать файлы markdown в качестве страниц, имеет смысл настроить unplugin на создание пустых путей для файлов с расширением .md.
Это можно сделать, используя параметр расширения в объекте VueRouter, чтобы добавить массив расширений файлов, которые вы хотите, чтобы плагин распознавал:
VueRouter({
// globally set the extensions
extensions: ['.vue', '.md'],
})
При такой конфигурации unplugin создаст пустые маршруты для каждого файла с расширениями .vue и .md в указанных папках маршрутов.
Вывод
Автоматическая маршрутизация на основе файлов может значительно повысить эффективность разработки. Простая интеграция и настройка упрощают изучение и использование.
В этом руководстве описаны основы, но есть и более продвинутые функции, которые вы можете изучить самостоятельно. Лучше всего начать с документации по unplugin-vue-router.