Система маршрутизации
В предыдущей главе наша логика управления задачами достигла персистентности с помощью Pinia. Теперь нам нужно вдохнуть душу в приложение —Система маршрутизации。
В разработке корпоративного уровня маршрутизация — это не только переключение страниц; она также принимает на себяБезопасность (контроль прав доступа)иПользовательский опыт (динамические заголовки)как важную ответственность.
Установка и базовая конфигурация
Сначала установите Vue Router:
npm install vue-router
ВsrcСоздайте каталог router в директории и создайтеrouter/index.jsфайл.

Мы определим две страницы: Dashboard (рабочая область) и Login (страница входа).
Пример
const routes = [
{
path: '/login',
name: 'Login',
component: () => import('@/views/Login.vue'),
meta: { title: 'Login - TaskHub', requiresAuth: false }
},
{
path: '/',
name: 'Dashboard',
component: () => import('@/views/Dashboard.vue'),
// Информация о метаданных маршрута meta: используется для хранения пользовательских данных
meta: {
title: 'Моя панель управления',
requiresAuth: true,
breadcrumb: 'Главная'
}
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
ВsrcСоздайте вviewsпапке и распределите страницы.
src/App.vue: корневой контейнер, помещайте только<router-view />и глобальные анимации.src/views/Dashboard.vue: оригинальные основные функции TaskHub (список задач, поле ввода и т. д.).src/views/Login.vue: недавно добавленная страница входа.

РефакторингApp.vue(Сделайте его чистым контейнером)
Пример
<div class="min-h-screen bg-slate-50">
<router-view v-slot="{ Component }">
<transition name="page" mode="out-in">
<component :is="Component" />
</transition>
</router-view>
</div>
</template>
<style>
/* Эффект затухания для переключения страниц */
.page-enter-active, .page-leave-active {
transition: opacity 0.2s ease;
}
.page-enter-from, .page-leave-to {
opacity: 0;
}
</style>
Перенесите исходную логику вDashboard.vue
Переместите код, ранее находившийся вApp.vueнаписанное внутри (импорт хранилища, импорт компонентов, разметка шаблона) всё скопируйте и вставьте вsrc/views/Dashboard.vue.
код файла Dashboard.vue
import { storeToRefs } from 'pinia'
import { useTaskStore } from '@/stores/taskStore'
import { useRouter } from 'vue-router' // Импорт роутера
import TaskHeader from '@/components/TaskHeader.vue'
import TaskInput from '@/components/TaskInput.vue'
import TaskFilter from '@/components/TaskFilter.vue'
import TaskItem from '@/components/TaskItem.vue'
const taskStore = useTaskStore()
const router = useRouter()
const { filter, filteredTasks } = storeToRefs(taskStore)
const { addTask, removeTask, toggleTask } = taskStore
// Метод выхода
const handleLogout = () => {
localStorage.removeItem('isLoggedIn')
router.push('/login')
}
</script>
<template>
<div class="py-12 px-4">
<div class="max-w-md mx-auto bg-white rounded-3xl shadow-xl border border-slate-100 overflow-hidden">
<TaskHeader />
<main class="p-6">
<TaskInput @add-task="addTask" />
<TaskFilter v-model="filter" />
<ul class="space-y-3">
<TransitionGroup name="list">
<TaskItem
v-for="task in filteredTasks"
:key="task.id"
:task="task"
@toggle="toggleTask"
@remove="removeTask"
/>
</TransitionGroup>
</ul>
<button
@click="handleLogout"
class="mt-8 w-full py-2 text-xs text-slate-400 hover:text-red-500 transition-colors"
>
Выйти из текущего аккаунта
</button>
</main>
</div>
</div>
</template>
Реализация логики входа (Login.vue)
Используйте Tailwind v4, чтобы быстро создать минимальную страницу входа и сымитировать поведение входа.
Пример
import { ref } from 'vue'
import { useRouter } from 'vue-router'
const router = useRouter()
const isLoading = ref(false)
const handleLogin = () => {
isLoading.value = true
// Имитация асинхронного запроса
setTimeout(() => {
localStorage.setItem('isLoggedIn', 'true')
router.push('/') // Перейти на главную страницу после успешного входа
isLoading.value = false
}, 1000)
}
</script>
<template>
<div class="min-h-screen flex items-center justify-center bg-slate-50">
<div class="p-8 bg-white rounded-3xl shadow-xl w-full max-w-sm border border-slate-100">
<h2 class="text-2xl font-black mb-6 text-slate-800">С возвращением</h2>
<button
@click="handleLogin"
:disabled="isLoading"
class="w-full bg-linear-to-r from-blue-600 to-indigo-600 text-white py-3 rounded-xl font-bold hover:opacity-90 active:scale-95 transition-all disabled:opacity-50"
>
{{ isLoading ? 'Выполняется вход...' : 'Войти в систему в один клик' }}
</button>
<p class="mt-4 text-center text-xs text-slate-400">Тестовая среда: нажмите, чтобы войти</p>
</div>
</div>
</template>
Улучшитьrouter/index.jsлогику перехвата
Убедитесь, что режим проверки безопасности включён в конфигурации маршрутов:
Пример
const routes = [
{
path: '/login',
name: 'Login',
component: () => import('@/views/Login.vue'),
meta: { title: 'Login - TaskHub', requiresAuth: false }
},
{
path: '/',
name: 'Dashboard',
component: () => import('@/views/Dashboard.vue'),
// Информация о метаданных маршрута meta: используется для хранения пользовательских данных
meta: {
title: 'Моя панель управления',
requiresAuth: true,
breadcrumb: 'Главная'
}
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
// src/router/index.js (основной фрагмент)
router.beforeEach((to, from, next) => {
const isAuthenticated = localStorage.getItem('isLoggedIn') === 'true'
// Если переходим на главную страницу, но не авторизованы -> перейти на страницу входа
if (to.meta.requiresAuth && !isAuthenticated) {
next('/login')
}
// Если уже авторизован, но всё ещё хочет перейти на страницу входа -> вернуться на главную
else if (to.path === '/login' && isAuthenticated) {
Далее('/')
}
else {
Далее()
}
})
export default router
Вmain.jsЗавершите последнюю часть в
Убедитесь, что вашmain.jsимпортирует и подключает router:
Пример
import { createPinia } from 'pinia'
import router from './router' // Импорт router
import App from './App.vue'
import './style.css'
const app = createApp(App)
app.use(createPinia())
app.use(router) // 2. Регистрация router
app.mount('#app')
Итоговый эффект
- Перейдите на /
:If you're not logged in, the page will flash briefly and then redirect to/login. - Перейдите на /login
:Click the button, local storageisLoggedInbecomestrue, затем мгновенно переключиться на дашборд. - Обновить страницу: Pinia прочитает задачи из LocalStorage, а страж маршрута проверит статус входа. Данные не будут потеряны.

После входа в систему вы можете нажать, чтобы выйти из системы:

Объяснение ключевых понятий
- Почему
App.vueстал пустым?
В одностраничном приложении (SPA),App.vueявляется оболочкой всего приложения. Мы сделали её пустой, чтобы динамически вставлять в неё разные страницы (Dashboard или Login) в зависимости от изменений URL. useRouterпрактического занятия:
ВDashboard.vueВ этом мы используемrouter.push('/login')чтобы реализовать функцию выхода. Примечание:pushэто добавит новую запись в историю браузера, и нажатие кнопки «Назад» в браузере позволит вернуться назад.- Наследование макета в Tailwind v4:
Поскольку мыApp.vueдобавили в глобальный контейнерbg-slate-50иmin-h-screen, все дочерние страницы (Dashboard, Login) по умолчанию унаследуют этот светло-серый фон, обеспечивая визуальную согласованность.