← Назад к вопросам
Как отрисовать прогресс перед вызовом router во Vue?
2.0 Middle🔥 202 комментариев
#Vue.js
Комментарии (2)
🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Отрисовка прогресс-бара перед навигацией в Vue
Это классическая задача UX в Single Page Applications. Когда пользователь кликает на ссылку, навигация может занять время, и нужно показать обратную связь.
Решение с использованием Navigation Guards
Vue Router предоставляет специальные хуки для контроля навигации. Самый эффективный способ — использовать глобальные guards:
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(),
routes: [
// маршруты...
]
})
// Хранилище для состояния прогресса
let progressState = { isLoading: false }
router.beforeEach((to, from, next) => {
progressState.isLoading = true
// Показываем прогресс-бар
window.dispatchEvent(new CustomEvent('progress-start'))
next()
})
router.afterEach(() => {
// Прячем прогресс-бар
progressState.isLoading = false
window.dispatchEvent(new CustomEvent('progress-end'))
})
export default router
Компонент прогресс-бара
Создаём простой компонент, который слушает события:
// components/ProgressBar.vue
<template>
<div v-if="isVisible" class="progress-bar">
<div class="progress-fill"></div>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
const isVisible = ref(false)
const handleStart = () => {
isVisible.value = true
}
const handleEnd = () => {
isVisible.value = false
}
onMounted(() => {
window.addEventListener('progress-start', handleStart)
window.addEventListener('progress-end', handleEnd)
})
onUnmounted(() => {
window.removeEventListener('progress-start', handleStart)
window.removeEventListener('progress-end', handleEnd)
})
</script>
<style scoped>
.progress-bar {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 3px;
background: linear-gradient(90deg, #3b82f6, #8b5cf6);
z-index: 9999;
animation: slide 0.8s ease-in-out;
}
.progress-fill {
height: 100%;
background: inherit;
animation: progress 2s ease-in-out infinite;
}
@keyframes slide {
0% { transform: translateX(-100%) }
100% { transform: translateX(0) }
}
@keyframes progress {
0% { width: 10% }
50% { width: 70% }
100% { width: 100% }
}
</style>
Альтернатива: с использованием Pinia
Если используешь Pinia, можешь централизовать управление состоянием:
// stores/progress.js
import { defineStore } from 'pinia'
import { ref } from 'vue'
export const useProgress = defineStore('progress', () => {
const isLoading = ref(false)
const start = () => { isLoading.value = true }
const end = () => { isLoading.value = false }
return { isLoading, start, end }
})
// main.js
const router = createRouter(...)
const pinia = createPinia()
router.beforeEach(() => {
useProgress().start()
})
router.afterEach(() => {
useProgress().end()
})
NProgress.js — готовое решение
Можешь использовать проверенную библиотеку:
npm install nprogress
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
router.beforeEach(() => NProgress.start())
router.afterEach(() => NProgress.done())
Это выглядит профессионально и требует минимум кода.
Когда использовать какой подход
- Простой кастомный — если нужна полная контроль над дизайном
- Pinia — в больших приложениях с complex state management
- NProgress — для быстрой реализации с минимум кода
Выбор зависит от сложности проекта и требований к UX.