# Frontend — Risques & vigilance : Performance > Extrait de la base de connaissance Lead_tech. Voir `knowledge/frontend/risques/README.md` pour l'index complet. --- ## Performances : sur-renders + bundle non maîtrisé ### Risques - App lente sur mobile - Bundle qui grossit sans contrôle - Chargements inutiles (images, libs) ### Symptômes - Input lag - Temps de chargement qui dérive à chaque feature - Requêtes réseaux inutiles ### Bonnes pratiques / mitigations - Lazy loading routes/features - Mesurer (au minimum) : temps de chargement + re-renders critiques - Politique images (formats, tailles, lazy) - Audit régulier des dépendances --- ## `useCallback` inutile quand le callback est wrappé en inline au render ### Risques - Le handler stable est re-wrappé dans une arrow inline lors du passage en prop → nouvelle référence à chaque render → `React.memo` ne peut pas éviter le re-render ### Symptômes ```tsx const handleToggle = useCallback((id: string) => { ... }, []); // stable ✓ // Mais au render : handleToggle(item.id)} /> // ↑ nouvelle closure à chaque render → memo inutile ``` ### Bonnes pratiques / mitigations - `useCallback` n'a de valeur que si le callback est passé **directement** en prop, sans re-wrapping - Si la signature doit capturer des variables de boucle, deux options : 1. Passer les données en props et laisser l'enfant appeler le handler avec ses propres props 2. Accepter que `memo` ne soit pas protégé et supprimer le `useCallback` inutile - Ne pas laisser un `useCallback` "pour faire bien" si son effet réel est nul - Contexte technique : React — app-template-resto 22-03-2026 --- ## Fetch sans timeout pour ressources lourdes ### Risques - `fetch()` sans timeout pour charger des blobs/fichiers volumineux (PDF, images) laisse l'utilisateur attendre indéfiniment sur connexion dégradée - Critique sur mobile avec connexion instable ; faux sentiment de freeze pour l'utilisateur ### Symptômes - `fetch(url)` sans `AbortController` + timeout pour des blobs/fichiers volumineux - Spinner infini sans message d'erreur ### Bonnes pratiques / mitigations - Utiliser `AbortController` avec `setTimeout` (15s recommandé) - Attraper `AbortError` et afficher un message explicite à l'utilisateur - **Signal review** : tout `fetch` de blob/fichier sans `AbortController` est suspect - Contexte technique : frontend / mobile — RL799_V2 06-04-2026 --- ## Bundle SPA non code-splitté — toute l'app livrée sur la page de login ### Risques - Imports statiques dans le router → tout le code applicatif (modales, panels admin, vues détail, workers PDF) est livré et parsé sur la page de login - DOMContentLoaded de plusieurs dizaines de secondes sur connexion mobile dégradée pour une page à 2 champs ; TTI massif au cold start ; les appareils anciens (CPU lent) paient le parse JS même en wifi - Anti-pattern transverse SPA (Vue Router, React Router, Angular, SvelteKit dev) ### Symptômes - Network DevTools (Slow 4G, cache vidé) sur `/login` : 300+ requêtes JS, 5+ Mo transférés, DOMContentLoaded > 30 s - La waterfall liste des composants d'écrans authentifiés (`Dashboard*`, `Admin*`, modales métier) - Lighthouse mobile : Performance < 50, LCP > 4s, TBT > 1s ### Signal de détection rapide - Network → "Disable cache" + "Preserve log" → Slow 4G → hard reload sur `/login`. Si on voit des composants d'écrans authentifiés dans les requêtes, le code splitting est absent ou cassé. ### Bonnes pratiques / mitigations ```ts // ✅ routes en imports dynamiques (Vite + Vue Router natif ; React Router lazy() ; Next auto par page) const routes = [{ path: '/login', component: () => import('@/pages/Login.vue') }]; // ✅ composants lourds non-critiques en async (modales, viewers PDF) const DocumentPreviewModal = defineAsyncComponent(() => import('@/components/DocumentPreviewModal.vue')); // ✅ préchargement post-login : pendant la saisie du mot de passe, pré-charger les chunks suivants import('@/pages/Dashboard.vue'); // dans Login.vue au mount, sans bloquer ``` - Auditer les barrel files (`export * from`) qui empêchent le tree-shaking → préférer `export { specificThing }` - **Mesurer avant/après en Slow 4G + 4× CPU throttling** (sans throttling on mesure son MacBook, pas l'usage réel) - Ordres de grandeur attendus (RL799 estimé) : requêtes login 336 → ~30-50, transfert 6.7 Mo → ~600 ko-1 Mo, DOMContentLoaded 51s → ~5-8s. Risque du fix très faible (mécanique sur le router), coût 2-4 h - Note : sur Next.js le splitting par page est automatique mais ne dispense PAS du `dynamic()` / `defineAsyncComponent` pour les composants lourds - Complète l'entrée "Performances : sur-renders + bundle non maîtrisé" par le signal Network observable et les chiffres - Contexte technique : Vue 3.5 / Vite 7 / Vue Router 4 — RL799_V2 (audit `/login`), 08-05-2026 --- ## Helper qui n'accepte qu'un ordre → force le caller à `reverse()` (O(n)×2 + clone) ### Risques - Un helper pur qui ne gère que l'ordre ASC force le caller à `helper([...arr].reverse()).reverse()` quand le store sert en DESC (pour `FlatList inverted`) - Coût caché : 2 × O(n) + 1 clone à CHAQUE re-render. Pour un tableau cumulatif (pagination), 500 items × 10 fetchs = 15000+ ops, invisible jusqu'à une conversation longue en prod ### Symptômes - `const result = helper([...store.items].reverse()).reverse();` ### Bonnes pratiques / mitigations ```ts // ✅ paramètre order : itère nativement à l'envers, 1 seul reverse final, 0 clone export function helper(items, opts, order: 'asc' | 'desc' = 'asc') { const iterate = order === 'asc' ? (cb) => { for (const m of items) cb(m); } : (cb) => { for (let i = items.length - 1; i >= 0; i--) cb(items[i]); }; // logique unique basée sur "previousDate", valable dans les deux sens return order === 'desc' ? result.reverse() : result; } ``` - Contexte technique : React Native — app-alexandrie (ux-cleanup-11 H2, `groupMessagesByTime`), 30-05-2026 --- ## Composant `renderItem` de `FlatList` sans `React.memo` → re-render à chaque page ### Risques - `FlatList` virtualise (n'instancie que les items visibles) mais ne mémoïze PAS les props : à chaque update de `data` (fetchNextPage cumulatif), tous les items visibles re-rendent même si leurs props sont inchangées - 100 items visibles → 100 re-renders inutiles à chaque page chargée ### Symptômes - Lag au scroll / au chargement de page sur une liste longue (DM, notif, contenu) ### Bonnes pratiques / mitigations ```tsx export const MessageBubble = React.memo(function MessageBubble(props) { /* … */ }); ``` - Tout composant rendu dans un `FlatList renderItem` doit être enveloppé dans `React.memo` (audit : grep `