# Phase G — Performance Analysis **Score Performance : 6.5/10** --- ## G1. Bundle ### Configuration build [vite.config.ts:68-92] - **Target** : `esnext` — navigation moderne uniquement - **Minification** : `esbuild` ✅ - **Source maps** : `hidden` en production (ne pas exposer le code source) ✅ - **Bundle analyzer** : `rollup-plugin-visualizer` en production → `dist/bundle-analysis.html` ✅ - **Chunk size warning** : 1000KB ### Manual chunks ```typescript // vite.config.ts:73-87 manualChunks: { 'vendor-react': ['react', 'react-dom'], 'vendor-router': ['react-router'], 'vendor-tanstack': ['@tanstack/*'], 'vendor-icons': ['lucide-react'], 'vendor-utils': ['date-fns', 'zod'], 'vendor': // Default vendor chunk } ``` **Positif** : Chunking explicite des dépendances pour un caching optimal. Les chunks vendeur changent rarement → longue durée de cache. ### Taille estimée [DONNÉES INSUFFISANTES — `npm run build` non exécuté. Estimation basée sur les dépendances :] | Chunk | Estimation | |-------|-----------| | `vendor-react` | ~140KB (gzip) | | `vendor-router` | ~25KB | | `vendor-tanstack` | ~40KB | | `vendor-icons` (lucide-react) | ~50-80KB (226 fichiers importent des icônes) | | `vendor-utils` (date-fns + zod) | ~30KB | | `vendor` (reste) | ~100KB+ (axios, framer-motion, i18next, etc.) | | Application code | ~200-300KB | | **Total estimé** | **~600-800KB** (gzip) | **Attention** : `lucide-react` (0.321.x) peut être volumineux si le tree-shaking n'est pas optimal. Les imports nommés (`import { Home } from 'lucide-react'`) sont utilisés → tree-shaking devrait fonctionner. **Attention** : `framer-motion` (12.29.x) est une dépendance lourde (~60KB gzip). Vérifier si toutes les animations nécessitent framer-motion ou si CSS animations (déjà définies dans index.css) suffiraient. --- ## G2. Code splitting ### React.lazy - **5 fichiers** utilisent `React.lazy()` / `lazy()` directement : - `features/chat/components/ChatInput.tsx:31` — emoji-picker-react ✅ - `features/chat/components/ChatMessage.tsx:11` — emoji-picker-react ✅ - `components/ui/ImageCropper.tsx:4` — Cropper ✅ - `components/ui/lazy-component/createLazyComponent.tsx:48` — factory function ✅ - `components/feedback/LazyToaster.tsx:15` — react-hot-toast ✅ ### Routes lazy-loadées **Toutes les routes** sont lazy via `createLazyComponent` [components/ui/lazy-component/createLazyComponent.tsx:48] qui utilise `React.lazy` avec error boundaries. Les exports sont centralisés dans `lazy-component/lazyExports.ts` [LazyComponent.tsx:39 → lazyExports.ts]. Routes lazy : Login, Register, Dashboard, Chat, Library, Profile, Settings, Sessions, Roles, TrackDetail, Playlists, Marketplace, Search, Notifications, Analytics, Webhooks, Admin, Social, Seller, Wishlist, Purchases, DesignSystem, 404, 500. ✅ **Excellent.** ### Dynamic imports - **60+ occurrences** de `import()` — usage approprié pour : - Route splitting - Heavy components (emoji-picker, cropper, swagger-ui) - Conditional loading (MSW, Sentry, toast) - Store lazy loading --- ## G3. Optimisation du rendu ### Memoization | Pattern | Occurrences | Verdict | |---------|------------|---------| | `useMemo` / `useCallback` | 135 fichiers | ✅ Usage correct | | `React.memo` | **5 fichiers seulement** | ⚠️ Insuffisant | **Composants avec `React.memo`** : - `PlaylistCard.tsx:203` ✅ - `TrackCard.tsx:198` ✅ - `CourseCard.tsx:136` ✅ - `PostCard.tsx:342` ✅ - `ProductCard.tsx:163` ✅ **Problème** : Seuls les cards sont mémorisés. Les composants de liste (TrackList, PlaylistList, etc.) qui itèrent sur ces cards ne sont PAS mémorisés. Les re-renders du parent provoquent des re-renders de tous les enfants. **-1 point.** ### `key={index}` anti-pattern **~55 occurrences** de `key={index}` ou `key={i}` ⚠️ Fichiers critiques (listes qui peuvent être réordonnées) : - `DashboardPage.tsx:248,303` — dashboard cards - `PlaybackHeatmapGrid.tsx:35` — grid dynamique - `TrackGrid.tsx:135` — grille de tracks - `TrackSearchResults.tsx:89` — résultats de recherche - `ChatMessage.tsx:98` — messages chat **Impact** : Pour les listes statiques (skeletons, options fixes), `key={index}` est acceptable. Pour les listes dynamiques (tracks, messages, résultats), c'est un bug potentiel de réconciliation React. ### Context providers - Le `QueryClientProvider` est au niveau racine [main.tsx:218] — normal - `ThemeProvider`, `AudioProvider`, `ToastProvider` sont au niveau App [App.tsx:170-187] — scope approprié - Pas de provider trop large provoquant des re-renders en cascade identifié ### `useEffect` instances - **~90 `useEffect` avec deps vides** `[]` — mount-only effects - La plupart sont des initialisations (fetch, event listeners). Quelques uns pourraient avoir des dépendances manquantes mais ESLint `exhaustive-deps` est configuré en `warn` [eslint.config.js]. --- ## G4. Assets ### Images - **`OptimizedImage`** composant dédié (145L) avec IntersectionObserver pour lazy loading [optimized-image/OptimizedImage.tsx] - **`BlurPlaceholder`** (33L) pour le placeholder pendant le chargement [optimized-image/BlurPlaceholder.tsx] - **`loading="lazy"` natif** : 0 occurrences ❌ — repose entièrement sur le JS custom - **WebP/AVIF** : Pas de détection de format optimisé côté client [DONNÉES INSUFFISANTES — dépend du backend/CDN] ### Fonts - 4 familles : Inter, Space Grotesk, JetBrains Mono, Noto Serif JP [index.css:78-81] - **Preload/display** : [DONNÉES INSUFFISANTES — nécessite vérification index.html] - **Subset** : Non détecté — potentiellement 4 polices complètes téléchargées ### SVG - 5 fichiers SVG dans `src/` — minimal - Lucide React injecte les icônes en inline SVG → pas de requêtes HTTP supplémentaires ✅ --- ## G5. Requêtes réseau ### Patterns de fetch - **Request deduplication** : `services/requestDeduplication.ts` — évite les appels API dupliqués ✅ - **Response caching** : `services/responseCache.ts` — cache en mémoire avec TTL ✅ - **Offline queue** : `services/offlineQueue.ts` — queue les mutations offline et les rejoue ✅ - **React Query cache** : staleTime 1min, gcTime 5min [main.tsx:49-50] - **Cross-tab sync** : `utils/reactQuerySync.ts` — synchronise le cache React Query entre onglets ✅ ### WebSocket - `services/websocket.ts` — WebSocket natif avec reconnection automatique ✅ - `features/streaming/hooks/usePlaybackRealtime.ts` (496L) — streaming temps réel avec analytics ✅ ### Waterfall - Les routes sont lazy-loadées, ce qui crée un waterfall initial (HTML → JS chunk → data fetch). C'est le pattern standard React SPA. - `useRoutePreload.ts` (239L) implémente le **prefetching des routes** au hover des liens ✅ — réduit le waterfall perçu. --- ## Score Performance détaillé | Critère | Points | Justification | |---------|--------|---------------| | Code splitting routes | +2 | Toutes les routes lazy, 60+ dynamic imports | | Manual chunks vendeur | +1 | Bon chunking pour cache long-terme | | React.memo insuffisant | -1 | 5 composants seulement, listes non mémorisées | | key={index} | -0.5 | 55 occurrences, certaines sur des listes dynamiques | | Request dedup + cache | +1 | Architecture réseau mature | | Route prefetching | +0.5 | useRoutePreload au hover | | useMemo/useCallback | +0.5 | 135 fichiers — bonne adoption | | Pas de loading="lazy" | -0.5 | Repose sur JS, pas d'attribut natif | | Fonts non optimisées | -0.5 | 4 familles, pas de subset/preload vérifié | | Offline queue | +0.5 | Fonctionnalité avancée | | framer-motion overhead | -0.5 | Dépendance lourde potentiellement surutilisée | | **Total** | **6.5/10** | **Infrastructure solide, optimisation rendu lacunaire** |