16 KiB
📚 Документация по рефакторингу HomePage.vue
🎯 Обзор
HomePage.vue - главный компонент приложения для управления игровым тиром. Прошел полный рефакторинг от монолитного компонента (2456 строк) до модульной архитектуры на Vue 3 Composition API.
📊 Метрики рефакторинга
| Метрика | До рефакторинга | После рефакторинга | Улучшение |
|---|---|---|---|
| Размер файла | 2456 строк | ~600 строк | -75% |
| Архитектура | Монолит | Модульная | ✅ |
| Поддерживаемость | Сложная | Легкая | ✅ |
| Тестируемость | Низкая | Высокая | ✅ |
| Переиспользование | Отсутствует | Максимальное | ✅ |
🏗️ Новая архитектура
Структура компонентов
HomePage.vue (главный компонент, ~600 строк)
├── Layout компоненты
│ ├── AppHeader.vue
│ ├── AppMain.vue
│ └── AppFooter.vue
├── UI компоненты
│ ├── IconButton.vue
│ ├── PlayPauseButton.vue
│ ├── ConfirmDialog.vue
│ ├── ThemeToggle.vue
│ ├── BurgerMenu.vue
│ └── TimeSemicircle.vue
├── Игровые компоненты
│ ├── GamePorts.vue
│ └── GamePort.vue
└── Компоненты смен
├── ShiftModal.vue
├── ShiftCloseModal.vue
└── ShiftForm/
├── FinancialSection.vue
├── ToysSection.vue
└── SalarySection.vue
Composables architecture
composables/
├── useAuth.js // Аутентификация и авторизация
├── game/
│ ├── useGamePorts.js // Управление игровыми портами
│ ├── useGameControl.js // Пауза/воспроизведение
│ └── useTime.js // Работа со временем
├── shift/
│ ├── useShifts.js // Статус смен
│ ├── useShiftOperations.js // Операции со сменами
│ └── useShiftForm.js // Валидация форм смен
└── ui/
├── useModals.js // Управление модальными окнами
├── useConfirm.js // Диалоги подтверждения
└── useBurgerMenu.js // Бургер меню
🔧 Технические решения
1. Composition API с <script setup>
Преимущества:
- Лучшая организация кода
- Логическое группирование связанного кода
- Улучшенная читаемость
- Легкое тестирование
Пример:
<script setup>
import { ref, computed, onMounted } from 'vue'
import { useRouter } from 'vue-router'
// Composables
import { useAuth } from '@/composables/useAuth'
import { useShiftOperations } from '@/composables/shift/useShiftOperations'
import { useModals } from '@/composables/ui/useModals'
// Инициализация
const router = useRouter()
const { user, logout, checkAuth } = useAuth()
const { shiftStatus, openRegularShift, submitCloseShift } = useShiftOperations()
const { showShiftModal, openShiftModal, closeShiftModal } = useModals()
</script>
2. Разделение ответственности
HomePage.vue теперь отвечает только за:
- Компоновку layout
- Координацию между компонентами
- Обработку основных событий
- Управление глобальным состоянием
Логика вынесена в:
- Composables для бизнес-логики
- Отдельные компоненты для UI
- Сервисы для работы с API
3. Управление состоянием
Глобальное состояние:
// useAuth.js - состояние аутентификации
const user = ref(null)
const isAuthenticated = computed(() => !!user.value)
// useShiftOperations.js - состояние смен
const shiftStatus = ref({
regularShift: null,
techShift: null
})
// useModals.js - состояние модальных окон
const showShiftModal = ref(false)
const showCloseShiftModal = ref(false)
Локальное состояние:
// HomePage.vue - только специфичное для страницы состояние
const isPaused = ref(false)
const closeShiftForm = ref({
money_konv: 0,
money_razm_closed: 0,
toys: [0, 0, 0, 0, 0]
})
4. Компонентная система
UI компоненты - переиспользуемые:
<!-- IconButton.vue -->
<template>
<button
class="icon-button"
:class="buttonClass"
:title="title"
@click="$emit('click')"
>
<slot>
<component :is="iconComponent" />
</slot>
</button>
</template>
<script setup>
defineProps({
icon: String,
title: String,
variant: { type: String, default: 'default' },
size: { type: String, default: 'md' }
})
defineEmits(['click'])
</script>
Бизнес-компоненты - предметно-ориентированные:
<!-- GamePorts.vue -->
<template>
<div class="game-ports">
<GamePort
v-for="port in ports"
:key="port.id"
:port="port"
@click="$emit('port-click', port.id)"
/>
</div>
</template>
<script setup>
import { useGamePorts } from '@/composables/game/useGamePorts'
const { ports, handlePortClick } = useGamePorts()
defineEmits(['port-click'])
</script>
🚀 Преимущества новой архитектуры
1. Поддерживаемость
- Четкая структура - каждый файл имеет свою зону ответственности
- Легкая навигация - логика сгруппирована по функциональности
- Самодокументируемый код - названия composables и компонентов говорят о их назначении
2. Тестируемость
- Изолированные единицы - каждый composable можно тестировать отдельно
- Моки и заглушки - легкая подмена зависимостей
- Покрытие кода - можно тестировать конкретную функциональность
3. Переиспользование
- UI компоненты - используются во всем приложении
- Composables - бизнес-логика доступна в разных компонентах
- Миксины функциональности - можно комбинировать разные composables
4. Производительность
- Ленивая загрузка - компоненты подгружаются по мере необходимости
- Оптимизация рендеринга - мелкие компоненты лучше оптимизируются Vue
- Меньший бандл - код разделяется на чанки
📋 Руководство по разработке
Добавление новой функциональности
- Создайте composable для бизнес-логики:
// composables/game/useNewFeature.js
export function useNewFeature() {
const state = ref(null)
const doSomething = () => {
// логика
}
return { state, doSomething }
}
- Создайте UI компонент при необходимости:
<!-- components/game/NewFeature.vue -->
<template>
<div class="new-feature">
<!-- UI -->
</div>
</template>
<script setup>
import { useNewFeature } from '@/composables/game/useNewFeature'
const { state, doSomething } = useNewFeature()
</script>
- Интегрируйте в HomePage.vue:
<!-- HomePage.vue -->
<template>
<NewFeature @some-event="handleEvent" />
</template>
<script setup>
import NewFeature from '@/components/game/NewFeature.vue'
const handleEvent = () => {
// обработка события
}
</script>
Модификация существующей функциональности
- Найдите соответствующий composable
- Измените логику в composable
- Обновите компоненты при необходимости
- Протестируйте изменения
Добавление нового UI компонента
-
Создайте компонент в соответствующей папке:
components/ui/- общие UI компонентыcomponents/game/- игровые компонентыcomponents/shift/- компоненты смен
-
Сделайте его переиспользуемым:
<script setup>
defineProps({
// props с валидацией
})
defineEmits([
// события
])
</script>
- Добавьте документацию и примеры использования
🧪 Тестирование
Тестирование composables
// tests/composables/useGamePorts.spec.js
import { useGamePorts } from '@/composables/game/useGamePorts'
describe('useGamePorts', () => {
it('должен инициализировать порты', () => {
const { gamePorts } = useGamePorts()
expect(gamePorts.value).toHaveLength(6)
})
it('должен обрабатывать клик по порту', () => {
const { handlePortClick } = useGamePorts()
const consoleSpy = vi.spyOn(console, 'log')
handlePortClick(1)
expect(consoleSpy).toHaveBeenCalledWith('Клик по порту:', 1)
})
})
Тестирование компонентов
// tests/components/GamePort.spec.js
import { mount } from '@vue/test-utils'
import GamePort from '@/components/game/GamePort.vue'
describe('GamePort', () => {
it('должен отображать номер порта', () => {
const port = { id: 1, active: false, occupied: false }
const wrapper = mount(GamePort, { props: { port } })
expect(wrapper.find('.port-number').text()).toBe('1')
})
it('должен иметь правильные классы для активного порта', () => {
const port = { id: 1, active: true, occupied: false }
const wrapper = mount(GamePort, { props: { port } })
expect(wrapper.classes()).toContain('active')
})
})
🔍 Best Practices
1. Структура composables
// ✅ Хорошо
export function useShiftOperations() {
// Состояние
const shiftStatus = ref(null)
// Методы
const openShift = async () => {
// логика
}
const closeShift = async () => {
// логика
}
// Возвращаем только необходимое
return {
shiftStatus,
openShift,
closeShift
}
}
2. Обработка ошибок
// ✅ Хорошо
const openShift = async () => {
try {
const response = await api.openShift()
return { success: true, data: response }
} catch (error) {
console.error('Ошибка открытия смены:', error)
return { success: false, error: error.message }
}
}
3. Валидация props
// ✅ Хорошо
const props = defineProps({
variant: {
type: String,
default: 'default',
validator: (value) => ['default', 'danger', 'warning'].includes(value)
},
size: {
type: String,
default: 'md',
validator: (value) => ['sm', 'md', 'lg'].includes(value)
}
})
4. Использование computed свойств
// ✅ Хорошо
const canManageShift = computed(() => {
return user.value?.group === 3 ||
user.value?.taccess === 'operators' ||
user.value?.taccess === 'admins'
})
🚨 Известные проблемы и решения
Проблема 1: Сложность отладки WebSocket
Решение: Создайте отдельный composable для логирования:
// composables/debug/useWebSocketLogger.js
export function useWebSocketLogger() {
const logMessage = (message, direction = 'send') => {
console.log(`[${direction.toUpperCase()}]`, new Date().toISOString(), message)
}
return { logMessage }
}
Проблема 2: Синхронизация состояния между компонентами
Решение: Используйте reactive объекты в composables:
// composables/shared/useGameState.js
const gameState = reactive({
isPaused: false,
activePorts: [],
currentShift: null
})
export function useGameState() {
return { gameState }
}
Проблема 3: Обработка множественных асинхронных операций
Решение: Используйте Promise.all или async/await с обработкой ошибок:
const initializeApp = async () => {
try {
const [authResult, shiftStatus, portsStatus] = await Promise.all([
checkAuth(),
getShiftsStatus(),
getPortsStatus()
])
return { success: true, data: { authResult, shiftStatus, portsStatus } }
} catch (error) {
return { success: false, error: error.message }
}
}
📈 План дальнейших улучшений
Краткосрочные цели (1-2 недели)
- Добавить TypeScript для типобезопасности
- Написать unit тесты для основных composables
- Оптимизировать производительность с помощью lazy loading
- Улучшить обработку ошибок и пользовательский опыт
Среднесрочные цели (1-2 месяца)
- Создать Storybook для документации компонентов
- Добавить E2E тесты для ключевых сценариев
- Реализовать кэширование для оптимизации запросов
- Улучшить мобильную версию
Долгосрочные цели (3-6 месяцев)
- Миграция на Pinia для управления состоянием
- Добавить PWA функциональность
- Оптимизировать бандл и время загрузки
- Реализовать темы оформления
📚 Полезные ресурсы
Документация
Инструменты
- Vue DevTools
- Vitest для тестирования
- ESLint для качества кода
- Prettier для форматирования
Паттерны и лучшие практики
🎉 Заключение
Рефакторинг HomePage.vue значительно улучшил архитектуру приложения:
- Уменьшен размер кода на 75%
- Повышена поддерживаемость через модульную структуру
- Улучшена тестируемость через изолированные composables
- Увеличено переиспользование компонентов и логики
Проект теперь следует современным практикам разработки на Vue 3 и готов к дальнейшему масштабированию и развитию.
Документация обновлена: {{ new Date().toLocaleDateString('ru-RU') }} §