add plan
This commit is contained in:
parent
da2a67bc91
commit
6e29b2e6df
|
|
@ -0,0 +1,816 @@
|
|||
# План рефакторинга app.js на ES6 модули
|
||||
|
||||
## Текущее состояние
|
||||
|
||||
- **Размер файла**: 1671 строка
|
||||
- **Проблемы**:
|
||||
- Монолитная структура затрудняет поддержку
|
||||
- Все функции в глобальной области видимости
|
||||
- Сложно тестировать отдельные компоненты
|
||||
- Повторное использование кода затруднено
|
||||
|
||||
## Целевая архитектура
|
||||
|
||||
```
|
||||
static/
|
||||
├── index.html
|
||||
├── styles.css
|
||||
├── js/
|
||||
│ ├── main.js # Точка входа, инициализация
|
||||
│ ├── config.js # Константы и конфигурация
|
||||
│ ├── state/
|
||||
│ │ └── appState.js # Глобальное состояние приложения
|
||||
│ ├── services/
|
||||
│ │ ├── api-client.js # Существующий API клиент (переместить)
|
||||
│ │ ├── auth.service.js # Аутентификация
|
||||
│ │ ├── settings.service.js # Управление настройками
|
||||
│ │ └── query.service.js # Запросы к RAG
|
||||
│ ├── ui/
|
||||
│ │ ├── auth.ui.js # UI авторизации
|
||||
│ │ ├── settings.ui.js # Диалог настроек
|
||||
│ │ ├── query-builder.ui.js # Построитель запросов
|
||||
│ │ ├── answer-viewer.ui.js # Просмотр ответов
|
||||
│ │ ├── questions-list.ui.js # Список вопросов
|
||||
│ │ ├── annotations.ui.js # Аннотации
|
||||
│ │ └── loading.ui.js # Индикаторы загрузки
|
||||
│ ├── utils/
|
||||
│ │ ├── dom.utils.js # DOM манипуляции
|
||||
│ │ ├── format.utils.js # Форматирование (время, текст)
|
||||
│ │ ├── file.utils.js # Работа с файлами
|
||||
│ │ └── validation.utils.js # Валидация данных
|
||||
│ └── data/
|
||||
│ ├── storage.js # LocalStorage обертка
|
||||
│ └── defaults.js # Дефолтные настройки (из settings.js)
|
||||
└── settings.js # Удалить после рефакторинга
|
||||
```
|
||||
|
||||
## Этапы рефакторинга
|
||||
|
||||
### Этап 1: Подготовка (день 1)
|
||||
|
||||
**Цель**: Настроить окружение для ES6 модулей
|
||||
|
||||
#### 1.1. Обновить index.html
|
||||
```html
|
||||
<!-- Заменить обычные скрипты на модули -->
|
||||
<script type="module" src="js/main.js"></script>
|
||||
```
|
||||
|
||||
#### 1.2. Создать структуру папок
|
||||
```bash
|
||||
mkdir static/js
|
||||
mkdir static/js/state
|
||||
mkdir static/js/services
|
||||
mkdir static/js/ui
|
||||
mkdir static/js/utils
|
||||
mkdir static/js/data
|
||||
```
|
||||
|
||||
#### 1.3. Настроить конфигурацию
|
||||
- Создать `js/config.js` с константами
|
||||
- Определить экспорты/импорты
|
||||
|
||||
---
|
||||
|
||||
### Этап 2: Утилиты и вспомогательные функции (день 1-2)
|
||||
|
||||
**Цель**: Вынести независимые функции
|
||||
|
||||
#### 2.1. `utils/format.utils.js`
|
||||
**Функции** (из app.js строки 150-188):
|
||||
- `generateUUID()`
|
||||
- `formatTime(seconds)`
|
||||
- `formatTimestamp(isoString)`
|
||||
- `isTableText(text)`
|
||||
- `parseTextTable(text)`
|
||||
- `escapeHtml(text)`
|
||||
|
||||
**Экспорт**:
|
||||
```javascript
|
||||
export {
|
||||
generateUUID,
|
||||
formatTime,
|
||||
formatTimestamp,
|
||||
isTableText,
|
||||
parseTextTable,
|
||||
escapeHtml
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.2. `utils/file.utils.js`
|
||||
**Функции** (строки 262-273, 984-1132):
|
||||
- `downloadJSON(data, filename)`
|
||||
- `loadFileAsJSON(file)` - новая функция для загрузки
|
||||
- `loadFileAsText(file)` - новая функция
|
||||
|
||||
**Экспорт**:
|
||||
```javascript
|
||||
export {
|
||||
downloadJSON,
|
||||
loadFileAsJSON,
|
||||
loadFileAsText
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.3. `utils/validation.utils.js`
|
||||
**Функции** (строки 823-860):
|
||||
- `validateJSON(jsonString)`
|
||||
- `validateLoginFormat(login)`
|
||||
|
||||
**Экспорт**:
|
||||
```javascript
|
||||
export {
|
||||
validateJSON,
|
||||
validateLoginFormat
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.4. `utils/dom.utils.js`
|
||||
**Функции**:
|
||||
- `showElement(id)`
|
||||
- `hideElement(id)`
|
||||
- `toggleElement(id)`
|
||||
- `setElementText(id, text)`
|
||||
- `showToast(message, type)` (строка 277-281)
|
||||
|
||||
**Экспорт**:
|
||||
```javascript
|
||||
export {
|
||||
showElement,
|
||||
hideElement,
|
||||
toggleElement,
|
||||
setElementText,
|
||||
showToast
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Этап 3: State Management (день 2)
|
||||
|
||||
**Цель**: Централизовать управление состоянием
|
||||
|
||||
#### 3.1. `state/appState.js`
|
||||
**Содержимое** (строки 10-42):
|
||||
```javascript
|
||||
class AppState {
|
||||
constructor() {
|
||||
this.settings = { /* ... */ }
|
||||
this.currentEnvironment = 'ift'
|
||||
this.environments = {
|
||||
ift: { /* ... */ },
|
||||
psi: { /* ... */ },
|
||||
prod: { /* ... */ }
|
||||
}
|
||||
}
|
||||
|
||||
// Геттеры
|
||||
getCurrentEnv() { /* ... */ }
|
||||
getCurrentEnvSettings() { /* ... */ }
|
||||
|
||||
// Сеттеры
|
||||
setCurrentEnvironment(env) { /* ... */ }
|
||||
updateSettings(settings) { /* ... */ }
|
||||
|
||||
// Persistence
|
||||
saveToLocalStorage() { /* ... */ }
|
||||
loadFromLocalStorage() { /* ... */ }
|
||||
}
|
||||
|
||||
export default new AppState()
|
||||
```
|
||||
|
||||
#### 3.2. `data/storage.js`
|
||||
**Функции**:
|
||||
- `saveEnvironmentData(env, data)`
|
||||
- `loadEnvironmentData(env)`
|
||||
- `clearEnvironmentData(env)`
|
||||
|
||||
**Экспорт**:
|
||||
```javascript
|
||||
export {
|
||||
saveEnvironmentData,
|
||||
loadEnvironmentData,
|
||||
clearEnvironmentData
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.3. `data/defaults.js`
|
||||
**Перенести из settings.js** (строки 1-70):
|
||||
```javascript
|
||||
export const defaultSettings = {
|
||||
activeEnvironment: 'ift',
|
||||
environments: { /* ... */ }
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Этап 4: Services (день 3)
|
||||
|
||||
**Цель**: Бизнес-логика и API взаимодействие
|
||||
|
||||
#### 4.1. `services/api-client.js`
|
||||
**Действие**: Переместить существующий `api-client.js` в папку `services/`
|
||||
|
||||
**Обновить**:
|
||||
```javascript
|
||||
class BriefBenchAPI {
|
||||
// ... существующий код
|
||||
}
|
||||
|
||||
export default new BriefBenchAPI()
|
||||
```
|
||||
|
||||
#### 4.2. `services/auth.service.js`
|
||||
**Функции** (строки 60-140):
|
||||
- `checkAuth()`
|
||||
- `login(loginString)`
|
||||
- `logout()`
|
||||
- `isAuthenticated()`
|
||||
|
||||
**Экспорт**:
|
||||
```javascript
|
||||
import api from './api-client.js'
|
||||
|
||||
export class AuthService {
|
||||
async checkAuth() { /* ... */ }
|
||||
async login(loginString) { /* ... */ }
|
||||
logout() { /* ... */ }
|
||||
isAuthenticated() { /* ... */ }
|
||||
}
|
||||
|
||||
export default new AuthService()
|
||||
```
|
||||
|
||||
#### 4.3. `services/settings.service.js`
|
||||
**Функции** (строки 290-357):
|
||||
- `loadSettingsFromServer()`
|
||||
- `saveSettingsToServer(settings)`
|
||||
- `extractEnvironmentSettings(envSettings)`
|
||||
- `resetToDefaults()`
|
||||
|
||||
**Экспорт**:
|
||||
```javascript
|
||||
import api from './api-client.js'
|
||||
import appState from '../state/appState.js'
|
||||
|
||||
export class SettingsService {
|
||||
async loadFromServer() { /* ... */ }
|
||||
async saveToServer(settings) { /* ... */ }
|
||||
extractEnvSettings(envSettings) { /* ... */ }
|
||||
resetToDefaults() { /* ... */ }
|
||||
}
|
||||
|
||||
export default new SettingsService()
|
||||
```
|
||||
|
||||
#### 4.4. `services/query.service.js`
|
||||
**Функции** (строки 861-1063):
|
||||
- `buildRequestBody()`
|
||||
- `sendQuery(environment, apiMode, requestBody)`
|
||||
- `extractQuestions()`
|
||||
- `loadRequestFromFile()`
|
||||
- `loadResponseFromFile()`
|
||||
|
||||
**Экспорт**:
|
||||
```javascript
|
||||
import api from './api-client.js'
|
||||
import appState from '../state/appState.js'
|
||||
|
||||
export class QueryService {
|
||||
buildRequestBody() { /* ... */ }
|
||||
async sendQuery(env, apiMode, body) { /* ... */ }
|
||||
extractQuestions() { /* ... */ }
|
||||
async loadRequestFromFile() { /* ... */ }
|
||||
async loadResponseFromFile() { /* ... */ }
|
||||
}
|
||||
|
||||
export default new QueryService()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Этап 5: UI Components (день 4-5)
|
||||
|
||||
**Цель**: Разделить UI логику по компонентам
|
||||
|
||||
#### 5.1. `ui/auth.ui.js`
|
||||
**Функции** (строки 77-132):
|
||||
- `showLoginScreen()`
|
||||
- `hideLoginScreen()`
|
||||
- `setupLoginListeners()`
|
||||
- `handleLoginSubmit()`
|
||||
|
||||
**Экспорт**:
|
||||
```javascript
|
||||
import authService from '../services/auth.service.js'
|
||||
|
||||
export class AuthUI {
|
||||
showLoginScreen() { /* ... */ }
|
||||
hideLoginScreen() { /* ... */ }
|
||||
setupListeners() { /* ... */ }
|
||||
async handleLoginSubmit() { /* ... */ }
|
||||
}
|
||||
|
||||
export default new AuthUI()
|
||||
```
|
||||
|
||||
#### 5.2. `ui/loading.ui.js`
|
||||
**Функции** (строки 1137-1145):
|
||||
- `showLoading(message)`
|
||||
- `hideLoading()`
|
||||
|
||||
**Экспорт**:
|
||||
```javascript
|
||||
export class LoadingUI {
|
||||
show(message) { /* ... */ }
|
||||
hide() { /* ... */ }
|
||||
}
|
||||
|
||||
export default new LoadingUI()
|
||||
```
|
||||
|
||||
#### 5.3. `ui/settings.ui.js`
|
||||
**Функции** (строки 362-813):
|
||||
- `openSettingsDialog()`
|
||||
- `closeSettingsDialog()`
|
||||
- `populateSettingsDialog()`
|
||||
- `readSettingsFromDialog()`
|
||||
- `toggleBackendSettings(show)`
|
||||
- `saveSettings()`
|
||||
- `resetSettings()`
|
||||
- `exportSettings()`
|
||||
- `importSettings()`
|
||||
|
||||
**Экспорт**:
|
||||
```javascript
|
||||
import settingsService from '../services/settings.service.js'
|
||||
import appState from '../state/appState.js'
|
||||
|
||||
export class SettingsUI {
|
||||
open() { /* ... */ }
|
||||
close() { /* ... */ }
|
||||
populate() { /* ... */ }
|
||||
read() { /* ... */ }
|
||||
toggleBackendSettings(show) { /* ... */ }
|
||||
async save() { /* ... */ }
|
||||
async reset() { /* ... */ }
|
||||
export() { /* ... */ }
|
||||
async import() { /* ... */ }
|
||||
setupListeners() { /* ... */ }
|
||||
}
|
||||
|
||||
export default new SettingsUI()
|
||||
```
|
||||
|
||||
#### 5.4. `ui/query-builder.ui.js`
|
||||
**Функции** (строки 643-883):
|
||||
- `showQueryBuilder()`
|
||||
- `switchQueryMode(mode)`
|
||||
- `validateJSON()`
|
||||
- `setupQueryBuilderListeners()`
|
||||
|
||||
**Экспорт**:
|
||||
```javascript
|
||||
import queryService from '../services/query.service.js'
|
||||
|
||||
export class QueryBuilderUI {
|
||||
show() { /* ... */ }
|
||||
switchMode(mode) { /* ... */ }
|
||||
validateJSON() { /* ... */ }
|
||||
setupListeners() { /* ... */ }
|
||||
async handleSendQuery() { /* ... */ }
|
||||
}
|
||||
|
||||
export default new QueryBuilderUI()
|
||||
```
|
||||
|
||||
#### 5.5. `ui/questions-list.ui.js`
|
||||
**Функции** (строки 1179-1273):
|
||||
- `renderQuestionsList()`
|
||||
- `selectAnswer(index)`
|
||||
- `updateQuestionsCount()`
|
||||
- `hasAnnotationsInDocs(docsSection)`
|
||||
- `pluralize(count, one, few, many)`
|
||||
|
||||
**Экспорт**:
|
||||
```javascript
|
||||
import appState from '../state/appState.js'
|
||||
|
||||
export class QuestionsListUI {
|
||||
render() { /* ... */ }
|
||||
selectAnswer(index) { /* ... */ }
|
||||
updateCount() { /* ... */ }
|
||||
hasAnnotations(docsSection) { /* ... */ }
|
||||
setupListeners() { /* ... */ }
|
||||
}
|
||||
|
||||
export default new QuestionsListUI()
|
||||
```
|
||||
|
||||
#### 5.6. `ui/answer-viewer.ui.js`
|
||||
**Функции** (строки 1279-1443):
|
||||
- `renderAnswer(index)`
|
||||
- `renderAnswerBody(elementId, text)`
|
||||
- `renderDocuments(containerId, docs, ...)`
|
||||
- `toggleExpansion(id)`
|
||||
- `switchTab(tabButton, tabId)`
|
||||
|
||||
**Экспорт**:
|
||||
```javascript
|
||||
import appState from '../state/appState.js'
|
||||
import { formatTime, parseTextTable, escapeHtml } from '../utils/format.utils.js'
|
||||
|
||||
export class AnswerViewerUI {
|
||||
render(index) { /* ... */ }
|
||||
renderBody(elementId, text) { /* ... */ }
|
||||
renderDocuments(containerId, docs, ...) { /* ... */ }
|
||||
toggleExpansion(id) { /* ... */ }
|
||||
switchTab(tabButton, tabId) { /* ... */ }
|
||||
setupListeners() { /* ... */ }
|
||||
}
|
||||
|
||||
export default new AnswerViewerUI()
|
||||
```
|
||||
|
||||
#### 5.7. `ui/annotations.ui.js`
|
||||
**Функции** (строки 1448-1615):
|
||||
- `initAnnotationForAnswer(index)`
|
||||
- `loadAnnotationsForAnswer(index)`
|
||||
- `loadSectionAnnotation(section, data)`
|
||||
- `loadDocumentAnnotations(section, subsection, docs)`
|
||||
- `setupAnnotationListeners()`
|
||||
- `updateCheckboxStyle(checkbox)`
|
||||
- `saveAnnotationsDraft()`
|
||||
|
||||
**Экспорт**:
|
||||
```javascript
|
||||
import appState from '../state/appState.js'
|
||||
import { saveEnvironmentData } from '../data/storage.js'
|
||||
|
||||
export class AnnotationsUI {
|
||||
initForAnswer(index) { /* ... */ }
|
||||
loadForAnswer(index) { /* ... */ }
|
||||
loadSection(section, data) { /* ... */ }
|
||||
loadDocuments(section, subsection, docs) { /* ... */ }
|
||||
setupListeners() { /* ... */ }
|
||||
saveDraft() { /* ... */ }
|
||||
}
|
||||
|
||||
export default new AnnotationsUI()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Этап 6: Main Entry Point (день 6)
|
||||
|
||||
**Цель**: Создать точку входа и инициализацию
|
||||
|
||||
#### 6.1. `js/main.js`
|
||||
```javascript
|
||||
// Импорты
|
||||
import appState from './state/appState.js'
|
||||
import authService from './services/auth.service.js'
|
||||
import settingsService from './services/settings.service.js'
|
||||
import authUI from './ui/auth.ui.js'
|
||||
import settingsUI from './ui/settings.ui.js'
|
||||
import queryBuilderUI from './ui/query-builder.ui.js'
|
||||
import questionsListUI from './ui/questions-list.ui.js'
|
||||
import answerViewerUI from './ui/answer-viewer.ui.js'
|
||||
import annotationsUI from './ui/annotations.ui.js'
|
||||
|
||||
// Инициализация приложения
|
||||
async function initApp() {
|
||||
// Load settings from server
|
||||
await settingsService.loadFromServer()
|
||||
appState.setCurrentEnvironment(appState.settings.activeEnvironment || 'ift')
|
||||
|
||||
// Load saved data for each environment
|
||||
appState.loadFromLocalStorage()
|
||||
|
||||
// Setup all UI listeners
|
||||
authUI.setupListeners()
|
||||
settingsUI.setupListeners()
|
||||
queryBuilderUI.setupListeners()
|
||||
questionsListUI.setupListeners()
|
||||
answerViewerUI.setupListeners()
|
||||
annotationsUI.setupListeners()
|
||||
|
||||
// Setup environment tabs
|
||||
setupEnvironmentTabs()
|
||||
|
||||
// Render initial state
|
||||
updateUI()
|
||||
}
|
||||
|
||||
// Setup environment tabs
|
||||
function setupEnvironmentTabs() {
|
||||
const tabs = document.querySelectorAll('.env-tab')
|
||||
tabs.forEach(tab => {
|
||||
tab.addEventListener('click', () => {
|
||||
switchEnvironment(tab.dataset.env)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Switch environment
|
||||
function switchEnvironment(env) {
|
||||
appState.setCurrentEnvironment(env)
|
||||
updateEnvironmentTabs()
|
||||
updateUI()
|
||||
}
|
||||
|
||||
// Update environment tabs
|
||||
function updateEnvironmentTabs() {
|
||||
const tabs = document.querySelectorAll('.env-tab')
|
||||
tabs.forEach(tab => {
|
||||
if (tab.dataset.env === appState.currentEnvironment) {
|
||||
tab.classList.add('active')
|
||||
} else {
|
||||
tab.classList.remove('active')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Update UI based on current state
|
||||
function updateUI() {
|
||||
questionsListUI.render()
|
||||
|
||||
const env = appState.getCurrentEnv()
|
||||
if (env.currentResponse && env.currentResponse.answers) {
|
||||
answerViewerUI.render(env.currentAnswerIndex || 0)
|
||||
} else {
|
||||
queryBuilderUI.show()
|
||||
}
|
||||
}
|
||||
|
||||
// Entry point
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
const isAuthenticated = await authService.checkAuth()
|
||||
|
||||
if (isAuthenticated) {
|
||||
await initApp()
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
#### 6.2. `js/config.js`
|
||||
```javascript
|
||||
// API Configuration
|
||||
export const API_CONFIG = {
|
||||
baseURL: '/api/v1',
|
||||
timeout: 1800000 // 30 minutes
|
||||
}
|
||||
|
||||
// UI Configuration
|
||||
export const UI_CONFIG = {
|
||||
defaultQueryMode: 'questions',
|
||||
defaultWithDocs: true,
|
||||
maxAnnotationLength: 5000
|
||||
}
|
||||
|
||||
// Storage Keys
|
||||
export const STORAGE_KEYS = {
|
||||
token: 'briefBenchToken',
|
||||
user: 'briefBenchUser',
|
||||
settings: 'briefBenchSettings',
|
||||
envData: (env) => `briefBenchData_${env}`,
|
||||
annotations: 'briefBenchAnnotationsDraft'
|
||||
}
|
||||
|
||||
// Constants
|
||||
export const ENVIRONMENTS = ['ift', 'psi', 'prod']
|
||||
export const API_MODES = ['bench', 'backend']
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Этап 7: Тестирование и оптимизация (день 7)
|
||||
|
||||
### 7.1. Ручное тестирование
|
||||
- ✅ Авторизация работает
|
||||
- ✅ Загрузка/сохранение настроек
|
||||
- ✅ Отправка запросов (bench/backend)
|
||||
- ✅ Отображение ответов
|
||||
- ✅ Аннотации сохраняются
|
||||
- ✅ Экспорт/импорт работает
|
||||
- ✅ Переключение окружений
|
||||
|
||||
### 7.2. Проверка производительности
|
||||
- Измерить время загрузки
|
||||
- Проверить размер бандла
|
||||
- Оптимизировать импорты
|
||||
|
||||
### 7.3. Очистка
|
||||
- Удалить старый `app.js`
|
||||
- Удалить `settings.js`
|
||||
- Обновить `.gitignore` если нужно
|
||||
|
||||
---
|
||||
|
||||
## Миграционная стратегия
|
||||
|
||||
### Вариант 1: Постепенная миграция (РЕКОМЕНДУЕТСЯ)
|
||||
|
||||
**Подход**: Модули живут параллельно с монолитом
|
||||
|
||||
1. Создать папку `js/` с модулями
|
||||
2. Оставить `app.js` работающим
|
||||
3. Постепенно переносить функции
|
||||
4. Тестировать после каждого этапа
|
||||
5. Когда все готово - переключиться на `main.js`
|
||||
|
||||
**Преимущества**:
|
||||
- Можно откатиться в любой момент
|
||||
- Меньше риска
|
||||
- Легче тестировать
|
||||
|
||||
**index.html во время миграции**:
|
||||
```html
|
||||
<!-- Старый код (работает) -->
|
||||
<script src="settings.js"></script>
|
||||
<script src="api-client.js"></script>
|
||||
<script src="app.js"></script>
|
||||
|
||||
<!-- Новый код (тестируется) -->
|
||||
<!-- <script type="module" src="js/main.js"></script> -->
|
||||
```
|
||||
|
||||
### Вариант 2: Быстрая миграция
|
||||
|
||||
**Подход**: Переписать всё за раз
|
||||
|
||||
**НЕ РЕКОМЕНДУЕТСЯ** из-за высокого риска багов
|
||||
|
||||
---
|
||||
|
||||
## Чек-лист миграции
|
||||
|
||||
### Подготовка
|
||||
- [ ] Создать ветку `feature/modularize-frontend`
|
||||
- [ ] Сделать backup текущего app.js
|
||||
- [ ] Создать структуру папок
|
||||
|
||||
### Этап 1: Утилиты
|
||||
- [ ] Создать `utils/format.utils.js`
|
||||
- [ ] Создать `utils/file.utils.js`
|
||||
- [ ] Создать `utils/validation.utils.js`
|
||||
- [ ] Создать `utils/dom.utils.js`
|
||||
- [ ] Тесты: проверить что функции работают
|
||||
|
||||
### Этап 2: State
|
||||
- [ ] Создать `state/appState.js`
|
||||
- [ ] Создать `data/storage.js`
|
||||
- [ ] Создать `data/defaults.js`
|
||||
- [ ] Тесты: проверить геттеры/сеттеры
|
||||
|
||||
### Этап 3: Services
|
||||
- [ ] Переместить `api-client.js` в `services/`
|
||||
- [ ] Создать `services/auth.service.js`
|
||||
- [ ] Создать `services/settings.service.js`
|
||||
- [ ] Создать `services/query.service.js`
|
||||
- [ ] Тесты: проверить API вызовы
|
||||
|
||||
### Этап 4: UI Components
|
||||
- [ ] Создать `ui/auth.ui.js`
|
||||
- [ ] Создать `ui/loading.ui.js`
|
||||
- [ ] Создать `ui/settings.ui.js`
|
||||
- [ ] Создать `ui/query-builder.ui.js`
|
||||
- [ ] Создать `ui/questions-list.ui.js`
|
||||
- [ ] Создать `ui/answer-viewer.ui.js`
|
||||
- [ ] Создать `ui/annotations.ui.js`
|
||||
- [ ] Тесты: проверить рендеринг
|
||||
|
||||
### Этап 5: Main
|
||||
- [ ] Создать `js/main.js`
|
||||
- [ ] Создать `js/config.js`
|
||||
- [ ] Обновить `index.html`
|
||||
- [ ] Тесты: полный цикл работы
|
||||
|
||||
### Этап 6: Очистка
|
||||
- [ ] Удалить старый `app.js`
|
||||
- [ ] Удалить `settings.js`
|
||||
- [ ] Обновить документацию
|
||||
- [ ] Code review
|
||||
|
||||
---
|
||||
|
||||
## Преимущества после рефакторинга
|
||||
|
||||
### Для разработки
|
||||
- ✅ **Модульность**: Каждый файл отвечает за свою область
|
||||
- ✅ **Тестируемость**: Легко покрыть модули unit-тестами
|
||||
- ✅ **Читаемость**: Проще найти нужную функцию
|
||||
- ✅ **Переиспользование**: Функции можно использовать в других проектах
|
||||
|
||||
### Для поддержки
|
||||
- ✅ **Изоляция**: Баг в одном модуле не затронет другие
|
||||
- ✅ **Масштабируемость**: Легко добавлять новые функции
|
||||
- ✅ **Документация**: Каждый модуль самодокументируемый
|
||||
- ✅ **Team work**: Разные разработчики могут работать над разными модулями
|
||||
|
||||
### Для производительности
|
||||
- ✅ **Tree shaking**: Неиспользуемый код не попадет в бандл
|
||||
- ✅ **Lazy loading**: Можно подгружать модули по требованию
|
||||
- ✅ **Кеширование**: Браузер может кешировать отдельные модули
|
||||
|
||||
---
|
||||
|
||||
## Риски и митигация
|
||||
|
||||
### Риск 1: Поломка существующего функционала
|
||||
**Митигация**: Постепенная миграция с тестированием после каждого этапа
|
||||
|
||||
### Риск 2: Увеличение времени загрузки (много файлов)
|
||||
**Митигация**: Использовать bundler (Vite/Webpack) для production
|
||||
|
||||
### Риск 3: Проблемы совместимости браузеров
|
||||
**Митигация**: ES6 модули поддерживаются всеми современными браузерами
|
||||
|
||||
### Риск 4: Сложность отладки
|
||||
**Митигация**: Source maps и понятная структура папок
|
||||
|
||||
---
|
||||
|
||||
## Следующие шаги
|
||||
|
||||
1. **Обсудить план** с командой
|
||||
2. **Выбрать стратегию миграции** (постепенная/быстрая)
|
||||
3. **Создать ветку** для рефакторинга
|
||||
4. **Начать с Этапа 1** (утилиты)
|
||||
5. **Тестировать** после каждого этапа
|
||||
6. **Code review** перед мержем
|
||||
|
||||
---
|
||||
|
||||
## Временные оценки
|
||||
|
||||
| Этап | Описание | Время |
|
||||
|------|----------|-------|
|
||||
| Этап 1 | Подготовка | 2 часа |
|
||||
| Этап 2 | Утилиты | 3 часа |
|
||||
| Этап 3 | State | 2 часа |
|
||||
| Этап 4 | Services | 4 часа |
|
||||
| Этап 5 | UI Components | 8 часов |
|
||||
| Этап 6 | Main Entry | 2 часа |
|
||||
| Этап 7 | Тестирование | 4 часа |
|
||||
| **ИТОГО** | | **~25 часов** (3-4 рабочих дня) |
|
||||
|
||||
---
|
||||
|
||||
## Пример использования после рефакторинга
|
||||
|
||||
```javascript
|
||||
// Было (app.js, строка 900):
|
||||
async function handleSendQuery() {
|
||||
const envSettings = getCurrentEnvSettings()
|
||||
const env = getCurrentEnv()
|
||||
const apiMode = envSettings.apiMode || 'bench'
|
||||
// ... 50 строк кода
|
||||
}
|
||||
|
||||
// Стало (js/ui/query-builder.ui.js):
|
||||
import queryService from '../services/query.service.js'
|
||||
import appState from '../state/appState.js'
|
||||
import loadingUI from './loading.ui.js'
|
||||
|
||||
export class QueryBuilderUI {
|
||||
async handleSendQuery() {
|
||||
const envSettings = appState.getCurrentEnvSettings()
|
||||
const apiMode = envSettings.apiMode || 'bench'
|
||||
|
||||
loadingUI.show('Отправка запроса...')
|
||||
|
||||
try {
|
||||
const result = await queryService.sendQuery(
|
||||
appState.currentEnvironment,
|
||||
apiMode,
|
||||
this.buildRequestBody()
|
||||
)
|
||||
|
||||
// Update state
|
||||
appState.getCurrentEnv().currentResponse = result.response
|
||||
|
||||
// Re-render
|
||||
this.hide()
|
||||
answerViewerUI.render(0)
|
||||
} catch (error) {
|
||||
showToast(error.message, 'error')
|
||||
} finally {
|
||||
loadingUI.hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Преимущества**:
|
||||
- Понятно где искать функцию
|
||||
- Легко тестировать
|
||||
- Явные зависимости
|
||||
- Переиспользуемый код
|
||||
|
||||
---
|
||||
|
||||
*Автор: Claude Sonnet 4.5*
|
||||
*Дата: 2025-12-25*
|
||||
Loading…
Reference in New Issue