Files
_Assistant_Lead_Tech/knowledge/frontend/risques/performance.md
T
MaksTinyWorkshop 5f5c87296e docs(knowledge): capitalisation frontend — intégration du triage local (mai-juin 2026)
Triage et intégration des propositions frontend du buffer 95_a_capitaliser.md
(lot local RL799_V2/Vue3 + app-alexandrie/RN-Expo, mai-juin 2026).

~73 entrées intégrées sur knowledge/frontend/ + 1 nouveau fichier, dont :
- patterns/state.md : race-token partagé latest-wins (fusion 3 props), capture sync anti-race,
  event bus timestamp, clé cache composite, état dérivé = computed
- risques/state.md : 9 risques Zustand/store (fetchId reset, useRef remount, re-fetch infini
  sur [], flag optimiste écrasé, cache détail/liste stale, latch sans reset, :key index)
- patterns/navigation.md : Expo Router (tab bar, useFocusEffect, Href typé, routing pur fusionné)
- patterns/general.md : helpers temps purs, composants génériques + skeleton, fail-fast, touch target
- risques/general.md : 24 risques (sweep statique, filtre client liste paginée, hooks avant return,
  a11y VoiceOver/disabled, redirection allowlist, RangeError toISOString, section i18n...)
- design-tokens (cluster theming light/dark MD3), tests, performance, react-native, nextjs
- NOUVEAU risques/responsive.md (gating par capacité d'input + checklist régressions mobile)
- READMEs patterns/risques mis à jour

Doublons inter-fichiers évités (vérifié : aucune ancre dupliquée introduite).
Rejets (doublons 91/9/87), reciblages workflow (156/257) et bloc 32 (CLAUDE projet) non intégrés ici.
Source 95_a_capitaliser.md non purgée (purge en fin de capitalisation complète).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 15:31:53 +02:00

7.3 KiB
Raw Blame History

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

const handleToggle = useCallback((id: string) => { ... }, []); // stable ✓

// Mais au render :
<ItemCard onToggle={() => 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

// ✅ 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

// ✅ 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

export const MessageBubble = React.memo(function MessageBubble(props) { /* … */ });
  • Tout composant rendu dans un FlatList renderItem doit être enveloppé dans React.memo (audit : grep <FlatList renderItem)
  • Si les handlers changent à chaque render parent : les mémoïser (useCallback) ou accepter le re-render. Comparateur custom rarement nécessaire si les props sont stables côté store
  • Contexte technique : React Native — app-alexandrie (ux-cleanup-11 M4/M5), 29-05-2026