veza/apps/web/dev_audit/frontend/07_performance_analysis.md

198 lines
7.4 KiB
Markdown
Raw Normal View History

# PHASE G — PERFORMANCE
---
## G1. Bundle
### Build status
- 🔴 **Le build échoue actuellement** — import manquant `educationService` dans `useEducationView.ts`
- Analyse basée sur le **dernier build réussi** dans `dist_verification/`
### Taille du build (dernier build disponible)
| Catégorie | Taille | Verdict |
|-----------|--------|---------|
| **Total dist/** | **6.8 MB** | ⚠️ Élevé (includes mockServiceWorker) |
| **JS total** | ~1.4 MB | ⚠️ Significatif |
| **CSS total** | ~150 KB | ✅ Raisonnable |
### Chunks JS (triés par taille)
| Chunk | Taille | Contenu |
|-------|--------|---------|
| `vendor-*.js` | **925 KB** | Dépendances générales (axios, zustand, framer-motion, etc.) |
| `index-*.js` | **113 KB** | Code applicatif principal |
| `vendor-react-*.js` | **85 KB** | React + React DOM |
| `vendor-utils-*.js` | **37 KB** | date-fns, zod |
| `routes-*.js` | **33 KB** | Route definitions |
| `vendor-tanstack-*.js` | **21 KB** | TanStack React Query |
| `TrackDetailPage-*.js` | **21 KB** | Page lazy-loaded |
| `vendor-icons-*.js` | **17 KB** | Lucide icons |
| `SettingsPage-*.js` | **17 KB** | Page lazy-loaded |
| `ChatPage-*.js` | **17 KB** | Page lazy-loaded |
### Problèmes identifiés
| Problème | Impact | Fichier |
|----------|--------|---------|
| `vendor-*.js` à 925 KB | 🔴 Chunk vendor monolithique trop gros | `vite.config.ts:85-98` |
| `mockServiceWorker.js` dans le build | ⚠️ 13 KB de MSW en production | Build output |
| `swagger-ui-react` en production deps | ⚠️ Potentiellement inclus dans vendor | `package.json` |
| `rollup-plugin-visualizer` en production deps | ⚠️ Devrait être en devDependencies | `package.json` |
### Tree-shaking
- ✅ Imports nommés pour `lucide-react` (vendor-icons à 17 KB — excellent pour ~50 icônes)
- ✅ Manual chunks configurés pour React, Router, TanStack, icons, utils
- ⚠️ Le chunk `vendor` de 925 KB suggère un tree-shaking incomplet des dépendances principales
- ⚠️ `emoji-picker-react` (~200 KB), `framer-motion` (~30 KB), `hls.js` (~50 KB) probablement dans vendor
### Source maps
-`sourcemap: 'hidden'` en production — source maps générées mais non exposées publiquement
---
## G2. Code splitting
### React.lazy()
| Fichier | Composant lazy | Usage |
|---------|---------------|-------|
| `components/ui/lazy-component/createLazyComponent.tsx:48` | Route components | ✅ Toutes les routes |
| `features/chat/components/ChatMessage.tsx:11` | EmojiPicker | ✅ Composant lourd |
| `features/chat/components/ChatInput.tsx:31` | EmojiPicker | ✅ Composant lourd |
| `components/ui/ImageCropper.tsx:4` | Cropper | ✅ Composant lourd |
| `components/feedback/LazyToaster.tsx:15` | Toaster | ✅ Non-critique |
### Routes lazy-loadées
-**100% des routes** sont lazy-loaded via `createLazyComponent`
- ✅ Chaque page a son propre chunk (TrackDetailPage-*.js, ChatPage-*.js, etc.)
### Composants lourds
| Composant | Lazy-loaded ? | Taille estimée |
|-----------|--------------|---------------|
| EmojiPicker | ✅ Oui | ~200 KB |
| ImageCropper | ✅ Oui | ~50 KB |
| SwaggerUI | ❌ **Non** | ~2 MB potentiel |
| Charts | ❌ **Non** | Variable |
| WaveformVisualizer | ❌ **Non** | ~20 KB |
**Problème** : `swagger-ui-react` n'est PAS lazy-loaded — s'il est importé dans un composant non-lazy, il pourrait gonfler le bundle principal.
---
## G3. Optimisation du rendu
### Memoization
| Hook | Instances | Verdict |
|------|-----------|---------|
| `useMemo` | ~50+ | ✅ Bien utilisé (filtered lists, expensive computations) |
| `useCallback` | ~80+ | ✅ Bien utilisé (event handlers, child callbacks) |
| `React.memo` | 5 | ⚠️ Sous-utilisé (TrackCard, PlaylistCard, CourseCard, ProductCard, PostCard) |
**`React.memo` manquant** :
- Les composants de liste (`TrackGrid`, `PlaylistList`) bénéficieraient de `React.memo` sur les items
- Les items des menus de navigation pourraient être memoizés
### useEffect
| Métrique | Valeur |
|----------|--------|
| Total useEffect | ~200+ instances |
| Fichiers avec 5+ useEffect | 2-3 (`App.tsx`, `usePlayer.ts`) |
⚠️ `App.tsx` avec 6+ useEffect est un signal de surcharge — devrait être décomposé.
### Context providers trop larges
- ⚠️ `AuthContext` et `ThemeContext` wrappent toute l'application — tout changement d'état provoque un re-render de l'arbre entier
- ✅ Zustand stores avec selectors ne causent pas de re-renders en cascade
- ✅ React Query cache est indépendant du rendu
### Listes sans key ou avec key={index}
- ⚠️ **100+ instances** de `key={index}` ou `key={i}`
- La plupart sont des **skeletons et listes statiques** — acceptable
- ⚠️ Certaines listes dynamiques (dashboard, chat messages) pourraient utiliser des IDs stables
---
## G4. Assets
### Images
-**Aucun `loading="lazy"`** détecté sur les `<img>`
- ✅ Composant `OptimizedImage` avec blur placeholder et fallback
- ⚠️ Pas de WebP/AVIF automatique (pas de pipeline d'optimisation d'images détecté)
- ⚠️ Pas de `srcset` ou `sizes` pour les images responsives
### Fonts
- ⚠️ Pas de `@font-face` déclarations — fonts chargées via Google Fonts (externe)
- ⚠️ Pas de `font-display: swap` explicite dans le CSS (dépend du chargement Google Fonts)
- ⚠️ Pas de `<link rel="preload">` pour les fonts
- Fonts utilisées : Barlow, Inter, Noto Sans JP, JetBrains Mono, Orbitron, Source Serif 4 — **6 familles de polices** est excessif
### SVG
- ✅ 5 fichiers SVG — faible impact
- ✅ Pas de SVG inlinés massifs détectés
---
## G5. Requêtes réseau
### Waterfall
- ✅ React Query avec `staleTime: 1min` évite les requêtes redondantes
- ✅ Request deduplication dans le client API
- ⚠️ Pas de prefetching détecté (pas de `queryClient.prefetchQuery`)
- ⚠️ Routes lazy-loaded + données = double waterfall (JS chunk + API call)
### Cache HTTP
- ✅ Response caching côté client dans le API client
- ✅ React Query gcTime de 5 minutes
- [DONNÉES INSUFFISANTES — headers Cache-Control côté serveur non analysés]
### WebSocket
- ✅ Reconnection automatique avec exponential backoff
- ✅ Max 5 tentatives de reconnection
- ⚠️ Pas de heartbeat/ping explicite détecté dans `websocket.ts`
---
## SCORE PERFORMANCE : 6/10
### Points gagnés
| Point | Score | Justification |
|-------|-------|---------------|
| 100% routes lazy-loaded | +1.5 | Excellent code splitting au niveau route |
| Manual chunks Vite | +0.8 | React, router, tanstack, icons séparés |
| React Query cache | +0.8 | staleTime, gcTime, deduplication |
| Memoization hooks | +0.7 | useMemo/useCallback bien utilisés |
| Composants lourds lazy | +0.5 | EmojiPicker, ImageCropper |
| Source maps hidden | +0.3 | Pas exposés en production |
| Virtualization | +0.4 | TanStack Virtual pour les listes longues |
### Points perdus
| Point | Score | Justification |
|-------|-------|---------------|
| Build cassé | -1.0 | Impossible de déployer |
| Vendor chunk 925 KB | -0.8 | Chunk monolithique trop gros |
| Pas de loading="lazy" images | -0.5 | Toutes les images chargées eagerly |
| 6 familles de fonts | -0.5 | Impact LCP et FOIT/FOUT |
| SwaggerUI non lazy | -0.3 | Potentiel 2 MB dans le bundle |
| React.memo sous-utilisé | -0.3 | 5 instances seulement |
| Pas de prefetching | -0.3 | Double waterfall routes + données |
| Pas de font preload | -0.2 | Blocage de rendu potentiel |
| key={index} fréquent | -0.1 | Minor mais présent |