Оптимизация сборки
Основной bundle Elftia был оптимизирован с 1,812 KB до 759 KB (-58%). В этом документе зафиксированы стратегии и стандарты оптимизации, чтобы предотвратить регрессии.
Ограничения размера bundle
Жесткие ограничения (обязательны к соблюдению)
| Метрика | Ограничение | Сейчас | Метод проверки |
|---|---|---|---|
| Максимальный single chunk | < 1 MB | 759 KB | npm run verify:build |
| Main bundle (gzipped) | < 300 KB | 221 KB | npm run verify:build |
| Предупреждения Vite | 0 | 0 | Вывод сборки |
Рекомендуемые цели
| Метрика | Цель | Сейчас | Приоритет |
|---|---|---|---|
| Main bundle (без сжатия) | < 500 KB | 759 KB | P1 |
| Общий bundle | < 3 MB | 6.24 MB | P2 |
| Загрузка первого экрана | < 2s | ~2s | P0 |
Команды проверки
# Быстрая проверка (4 ключевые метрики)
npm run verify:build
# Подробный анализ (статистика категоризации chunk)
npm run analyze:build
# Визуальный анализ (сгенерировать stats.html)
npm run build:renderer
# Open dist/stats.html
Стратегия code splitting
Code splitting на уровне маршрутов (обязательно)
Все page components должны использовать React.lazy для lazy loading. lazy/ — это каталог, организованный по назначению (pages.tsx / workspaces.tsx / inline.tsx / shell.tsx / skeletons.tsx / with-suspense.tsx / index.ts barrel); добавляйте новую страницу, изменяя lazy/pages.tsx:
// packages/renderer/src/app/lazy/pages.tsx
export const LazySettingsPage = lazy(() => import('../Settings'));
export const SuspenseSettings = withSuspense(LazySettingsPage, PageSkeleton);
// packages/renderer/src/app/App.tsx — old import paths still valid (barrel re-export)
import { SuspenseSettings as Settings } from './components/lazy';
<Route path="/settings" element={<Settings />} />
// Don't do this: direct import of page components
import Settings from './components/Settings';
<Route path="/settings" element={<Settings />} />
Lazy loading крупных компонентов (рекомендуется)
Используйте lazy loading, если один компонент > 100 KB или зависит от крупных сторонних библиотек.
Компоненты, уже загружаемые через lazy loading:
| Component | Размер | Зависимость |
|---|---|---|
MermaidDiagram | 451 KB | mermaid |
HtmlPreviewPanel | 198 KB | iframe sandbox |
CodeEditor | 34 KB | CodeMirror |
Shell / StandaloneShell | 9 KB | xterm |
Критерии принятия решения:
| Условие | Lazy Load? |
|---|---|
| Component bundle > 100 KB | Да |
| Зависит от крупной сторонней библиотеки | Да |
| Не нужен для первого экрана | Да |
| Низкая частота использования | Да |
| Небольшой общий компонент (< 10 KB) | Нет |
| Core component, нужный для первого экрана | Нет |
| Часто переключаемый UI component | Нет |
Оптимизация Context
Переносите Context, который не требуется глобально, в компоненты уровня страницы:
// Good: page-level Context
export function Settings() {
return (
<SettingsProvider>
<SettingsContent />
</SettingsProvider>
);
}
// Bad: load unnecessary Context globally
<GlobalContext>
<Routes />
</GlobalContext>
Перед переносом Context обязательно проанализируйте зависимости, чтобы не нарушить совместное использование состояния между страницами.
Конфигурация vendor chunks
Текущая стратегия группирует по частоте обновлений и частоте использования:
// vite.config.js — manualChunks
{
'vendor-react': ['react', 'react-dom', 'react-router-dom'],
'vendor-ui': ['@radix-ui/react-context-menu', '@radix-ui/react-dialog', ...],
'vendor-icons': ['lucide-react'],
'vendor-utils': ['clsx', 'tailwind-merge', 'class-variance-authority', 'zustand'],
'vendor-markdown': ['react-markdown', 'remark-gfm', 'rehype-highlight'],
'vendor-codemirror': ['@codemirror/state', '@codemirror/view', ...],
'vendor-xterm': ['@xterm/xterm', '@xterm/addon-fit', '@xterm/addon-web-links'],
}
Принципы группировки
| Тип | Частота обновлений | Приоритет cache | Примеры |
|---|---|---|---|
| Core frameworks | Низкая | Наивысший | React, React DOM |
| Крупные сторонние libs | Низкая | Высокий | CodeMirror, xterm |
| UI component library | Средняя | Средний | Radix UI, lucide |
| Utility libraries | Средняя | Средний | clsx, zustand |
Проверка новой зависимости
При добавлении новых сторонних зависимостей:
# 1. Check size
npm info <package> dist.unpackedSize
# 2. If > 100 KB, add to vendor chunks
# 3. If high update frequency, create separate vendor chunk
# 4. Verify tree shaking support
# 5. Run npm run analyze:build to check impact
Tree Shaking
Правильные способы import
// Good: named imports (supports tree shaking)
import { Button, Input, Select } from '@/components/ui';
import { Home, Settings, User } from 'lucide-react';
// Good: Radix UI namespace imports (official recommendation)
import * as Dialog from '@radix-ui/react-dialog';
// Bad: import entire library
import * as UI from '@/components/ui';
import * as Icons from 'lucide-react';
Проверка неиспользуемых imports
npm run typecheck # TypeScript detects unused imports
npm run lint:eslint # ESLint warns about unused variables
Конфигурация production-сборки Vite
// vite.config.js — key configuration
build: {
minify: 'terser', // terser has better compression than esbuild
sourcemap: false, // disable sourcemap in production
chunkSizeWarningLimit: 500, // chunk size warning threshold (KB)
terserOptions: {
compress: {
drop_console: true, // remove console.log (keep warn/error)
drop_debugger: true, // remove debugger
},
},
}
Распространенные проблемы и решения
Main bundle слишком большой (> 500 KB)
Шаги расследования:
- Выполните
npm run build:renderer, откройтеdist/stats.html - Проверьте состав main bundle, найдите самые крупные modules
- Проверьте, не отсутствует ли lazy loading у крупных компонентов
- Проверьте наличие ненужных глобальных imports
Решения:
- Переведите крупные компоненты на lazy loading
- Удалите неиспользуемые imports
- Переведите page components на lazy loading уровня маршрутов
Vendor chunks слишком большие (> 500 KB)
Решения:
- Разделите крупные vendors на меньшие chunks
- Перегруппируйте по частоте обновлений
- Проверьте наличие дублирующихся зависимостей
Общий bundle слишком большой (> 5 MB)
Решения:
- Загружайте библиотеки графиков через lazy loading (Mermaid, Cytoscape и т. д.)
- Удалите редко используемые функции или зависимости
- Рассмотрите использование более компактных альтернативных библиотек
Code splitting не сработал
Шаги расследования:
- Проверьте конфигурацию lazy loading для соответствующих split-файлов в каталоге
lazy/(уровень страниц вlazy/pages.tsx, тела workspace вlazy/workspaces.tsx, inline chat components вlazy/inline.tsx) - Проверьте, используются ли lazy-loaded components в
App.tsx - Проверьте
manualChunksв конфигурации Vite
Pre-Commit checklist
При добавлении новых страниц
- Page component добавлен в
lazy/pages.tsx(или соответствующий split-файл) - Обернут в
withSuspenseи имеет fallback - В
App.tsxиспользуется lazy-loaded версия - Выполнен
npm run build:rendererдля проверки отдельного chunk
При добавлении крупных компонентов
- Размер компонента > 100 KB → используйте lazy loading
- Зависит от крупной сторонней библиотеки → используйте lazy loading
- Предоставлено подходящее состояние загрузки
При добавлении сторонних зависимостей
- Проверьте размер зависимости
- Зависимость > 100 KB → добавьте в vendor chunks
- Высокая частота обновлений → отдельный vendor chunk
- Поддерживает tree shaking → используйте named imports
- Выполните
npm run analyze:build, чтобы проверить влияние
Рекомендации по непрерывной оптимизации
Перед каждым release
npm run verify:build
npm run build:renderer | grep -i "warning"
# If main bundle increased > 50 KB, investigate reason
Ежемесячно
npm run build:renderer
# Open dist/stats.html to check optimizable modules
npm outdated
npm update
Ежеквартально
- Пересматривайте всю конфигурацию vendor chunks
- Оценивайте новые стратегии оптимизации
- Оценивайте, стоит ли удалить редко используемые функции
- Обновляйте этот документ стандартов
Интеграция CI/CD
Рекомендуется добавить проверки размера bundle в CI:
- name: Build and verify
run: |
npm run build:renderer
npm run verify:build || echo "Warning: Bundle size check failed"
Базовые метрики производительности
| Метрика | Базовое значение | Цель | Мониторинг |
|---|---|---|---|
| Main bundle | 759 KB | < 500 KB | verify:build |
| Загрузка первого экрана | ~2s | < 2s | Lighthouse |
| Общий bundle | 6.24 MB | < 3 MB | analyze:build |
| Количество chunks | 76 | - | analyze:build |
Справочные ресурсы
- Vite - Build Optimizations
- React.lazy - Code Splitting
- Rollup - Code Splitting
- rollup-plugin-visualizer — визуализация bundle