# 🚫 Anti-Patterns à Éviter - Guide Stabilité

## ❌ JAMAIS faire ça

### 1. Timer sans cleanup

**❌ MAUVAIS:**
```typescript
// Component.vue
onMounted(() => {
  setInterval(() => {
    fetchData()
  }, 5000)
})
// 🔥 FUITE MÉMOIRE! Timer continue après unmount
```

**✅ BON:**
```typescript
import { onMounted, onUnmounted } from 'vue'

let timer: ReturnType<typeof setInterval> | null = null

onMounted(() => {
  timer = setInterval(() => {
    fetchData()
  }, 5000)
})

onUnmounted(() => {
  if (timer) {
    clearInterval(timer)
    timer = null
  }
})
```

**✅ MEILLEUR (avec utilitaire):**
```typescript
import { createCleanupManager } from '@/shared/utils/performanceOptimizer'
import { onUnmounted } from 'vue'

const cleanup = createCleanupManager()

onMounted(() => {
  cleanup.setInterval(() => fetchData(), 5000)
})

onUnmounted(() => {
  cleanup.cleanup() // Nettoie TOUT automatiquement
})
```

---

### 2. Event listener sans cleanup

**❌ MAUVAIS:**
```typescript
onMounted(() => {
  window.addEventListener('resize', handleResize)
})
// 🔥 FUITE MÉMOIRE! Listener reste après unmount
```

**✅ BON:**
```typescript
onMounted(() => {
  window.addEventListener('resize', handleResize)
})

onUnmounted(() => {
  window.removeEventListener('resize', handleResize)
})
```

**✅ MEILLEUR (avec utilitaire):**
```typescript
const cleanup = createCleanupManager()

onMounted(() => {
  cleanup.addEventListener(window, 'resize', handleResize)
})

onUnmounted(() => {
  cleanup.cleanup()
})
```

---

### 3. Polling sans pause

**❌ MAUVAIS:**
```typescript
// Poll non-stop même si onglet caché
setInterval(() => {
  fetch('/api/notifications')
}, 30000)
// 🔥 GASPILLAGE RÉSEAU + BATTERIE!
```

**✅ BON:**
```typescript
let isPageVisible = !document.hidden
let pollingTimer: ReturnType<typeof setInterval> | null = null

function startPolling() {
  if (!isPageVisible) return
  
  pollingTimer = setInterval(() => {
    if (isPageVisible) {
      fetch('/api/notifications')
    }
  }, 60000) // Réduit à 60s
}

function handleVisibilityChange() {
  isPageVisible = !document.hidden
  
  if (isPageVisible) {
    startPolling()
  } else {
    if (pollingTimer) {
      clearInterval(pollingTimer)
      pollingTimer = null
    }
  }
}

onMounted(() => {
  document.addEventListener('visibilitychange', handleVisibilityChange)
  startPolling()
})

onUnmounted(() => {
  document.removeEventListener('visibilitychange', handleVisibilityChange)
  if (pollingTimer) {
    clearInterval(pollingTimer)
  }
})
```

---

### 4. Animations qui causent layout thrashing

**❌ MAUVAIS:**
```css
/* Force recalcul layout à chaque frame */
.element {
  transition: width 0.3s, height 0.3s, top 0.3s, left 0.3s;
}

.element:hover {
  width: 120%;
  height: 120%;
  top: -10px;
  left: -10px;
}
/* 🔥 FLICKERING garanti! */
```

**✅ BON:**
```css
/* GPU-accelerated, pas de recalcul layout */
.element {
  transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1),
              opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  transform: translateZ(0); /* Force GPU layer */
}

.element:hover {
  transform: scale(1.2) translateZ(0);
  opacity: 0.9;
}
/* ✨ Smooth comme du beurre */
```

---

### 5. will-change permanent

**❌ MAUVAIS:**
```css
/* Monopolise RAM GPU en permanence */
.element {
  will-change: transform, opacity, width, height;
}
/* 🔥 GASPILLAGE GPU! */
```

**✅ BON:**
```css
/* Utilise will-change uniquement pendant animation */
.element {
  transition: transform 0.3s;
}

.element.is-animating {
  will-change: transform;
}

.element:not(.is-animating) {
  will-change: auto; /* Libère ressources */
}
```

---

### 6. Refs qui update trop souvent

**❌ MAUVAIS:**
```typescript
// Update 60x/seconde au scroll
const scrollY = ref(0)

onMounted(() => {
  window.addEventListener('scroll', () => {
    scrollY.value = window.scrollY // 🔥 60 re-renders/seconde!
  })
})
```

**✅ BON:**
```typescript
import { useDebouncedRef } from '@/shared/utils/performanceOptimizer'

// Update max 1x/300ms
const scrollY = ref(0)
const debouncedScrollY = useDebouncedRef(scrollY, 300)

// Ou avec throttle pour mise à jour régulière
const throttledScrollY = useThrottledRef(scrollY, 100) // Max 10x/seconde
```

---

### 7. Computed sans cache

**❌ MAUVAIS:**
```typescript
// Recalcule à chaque accès
function getTotalDistance() {
  return stages.value.reduce((sum, stage) => sum + stage.distance, 0)
}

// Appelé dans template
<div>{{ getTotalDistance() }}</div> // 🔥 Recalcul à chaque render!
```

**✅ BON:**
```typescript
// Cached automatiquement par Vue
const totalDistance = computed(() => {
  return stages.value.reduce((sum, stage) => sum + stage.distance, 0)
})

<div>{{ totalDistance }}</div> // ✨ Recalcul uniquement si stages change
```

---

### 8. Watchers qui déclenchent des boucles

**❌ MAUVAIS:**
```typescript
watch(planId, async (newId) => {
  const plan = await fetchPlan(newId)
  currentPlan.value = plan
})

watch(currentPlan, (newPlan) => {
  if (newPlan) {
    planId.value = newPlan.id // 🔥 BOUCLE INFINIE!
  }
})
```

**✅ BON:**
```typescript
// Option 1: flush timing
watch(planId, async (newId) => {
  const plan = await fetchPlan(newId)
  currentPlan.value = plan
}, { flush: 'post' })

// Option 2: Guard explicite
let isUpdatingFromWatch = false

watch(planId, async (newId) => {
  if (isUpdatingFromWatch) return
  isUpdatingFromWatch = true
  
  const plan = await fetchPlan(newId)
  currentPlan.value = plan
  
  isUpdatingFromWatch = false
})
```

---

### 9. Fetch sans abort controller

**❌ MAUVAIS:**
```typescript
async function search(query: string) {
  const results = await fetch(`/api/search?q=${query}`)
  return results.json()
}

// User tape "Paris" → 5 requêtes en parallèle (P, Pa, Par, Pari, Paris)
// 🔥 SPAM RÉSEAU!
```

**✅ BON:**
```typescript
let abortController: AbortController | null = null

async function search(query: string) {
  // Annule requête précédente
  if (abortController) {
    abortController.abort()
  }
  
  abortController = new AbortController()
  
  try {
    const results = await fetch(`/api/search?q=${query}`, {
      signal: abortController.signal
    })
    return results.json()
  } catch (error) {
    if (error.name === 'AbortError') {
      return null // Normal, requête annulée
    }
    throw error
  }
}

// Ou mieux: avec debounce
import { useDebounceFn } from '@vueuse/core'

const debouncedSearch = useDebounceFn(search, 300)
```

---

### 10. Virtual scroll fait maison

**❌ MAUVAIS:**
```vue
<!-- Render 1000 items en brut -->
<div v-for="item in items" :key="item.id">
  {{ item.name }}
</div>
<!-- 🔥 LAG au scroll! -->
```

**✅ BON:**
```vue
<!-- Option 1: Limit visible items -->
<div v-for="item in visibleItems" :key="item.id">
  {{ item.name }}
</div>

<script setup>
import { computed } from 'vue'

const visibleItems = computed(() => {
  return items.value.slice(0, 50) // Render uniquement 50
})
</script>

<!-- Option 2: Virtual scroll library -->
<RecycleScroller
  :items="items"
  :item-size="60"
  key-field="id"
>
  <template #default="{ item }">
    <div>{{ item.name }}</div>
  </template>
</RecycleScroller>
```

---

## 🎯 Checklist avant commit

Avant de push du code, vérifier:

- [ ] Tous les `setInterval` ont un `clearInterval`
- [ ] Tous les `setTimeout` ont un `clearTimeout`
- [ ] Tous les `addEventListener` ont un `removeEventListener`
- [ ] Polling utilise Page Visibility API
- [ ] Animations utilisent `transform` + `opacity` uniquement
- [ ] `will-change` est temporaire (pas permanent)
- [ ] Pas de computed sans cache
- [ ] Pas de watchers en boucle
- [ ] Fetch avec AbortController si applicable
- [ ] Listes longues (100+) utilisent virtual scroll

---

## 🔍 Comment détecter ces problèmes

### Chrome DevTools

1. **Memory Profiler:**
   - Prendre 2 snapshots à 5 min d'intervalle
   - Si diff > 50MB → fuite mémoire

2. **Performance:**
   - Enregistrer 10s de scroll
   - Chercher "Long Tasks" > 50ms
   - Chercher "Layout Shift" > 0.1

3. **Network:**
   - Filtrer par XHR
   - Observer 1 minute
   - Si > 10 req/min → polling excessif

### Console logs

```typescript
// En dev uniquement
if (import.meta.env.DEV) {
  // Log timers actifs
  console.log('[Timers]', {
    intervals: setInterval.length, // Pas standard mais utile
    timeouts: setTimeout.length
  })
  
  // Log memory
  console.log('[Memory]', performance.memory)
}
```

---

## 📚 Ressources

- [Vue Performance](https://vuejs.org/guide/best-practices/performance.html)
- [Web Vitals](https://web.dev/vitals/)
- [CSS Triggers](https://csstriggers.com/)
- [requestIdleCallback](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback)

---

**Rappel:** Un bon code n'a pas besoin d'optimisation, il est optimisé dès le départ. Suivez ces patterns et votre app sera stable par défaut.
