Retour au blog

Debugging JavaScript : Techniques Avancées avec DevTools Que Vous Devez Connaître

Salut HaWkers, le debugging est l'une des compétences les plus importantes pour tout développeur, mais beaucoup se limitent encore au console.log(). Les outils de développeur des navigateurs modernes offrent des fonctionnalités puissantes qui peuvent complètement transformer votre façon de trouver et corriger les bugs.

Explorons des techniques avancées qui vont au-delà des bases ?

Au-delà du console.log()

Le console.log() est utile, mais l'objet console offre beaucoup plus :

Console Methods Avancés

// Grouper les logs liés
console.group('Traitement Utilisateur');
console.log('Nom:', user.name);
console.log('Email:', user.email);
console.log('Permissions:', user.permissions);
console.groupEnd();

// Tables pour arrays et objets
const users = [
  { name: 'Ana', age: 28, role: 'Dev' },
  { name: 'Carlos', age: 32, role: 'Lead' },
  { name: 'Maria', age: 25, role: 'Dev' }
];
console.table(users);

// Mesurer le temps d'exécution
console.time('fetchUsers');
await fetchUsers();
console.timeEnd('fetchUsers'); // fetchUsers: 234.5ms

// Comptage d'exécutions
function processItem(item) {
  console.count('processItem appelé');
  // logique...
}

// Stack trace sans erreur
function deepFunction() {
  console.trace('Comment sommes-nous arrivés ici ?');
}

// Assertions pour debugging
console.assert(user.id > 0, 'User ID doit être positif', user);

Console Styling

// Logs colorés et stylisés
console.log(
  '%c ERREUR %c Échec d\'authentification',
  'background: #ff0000; color: white; padding: 2px 6px; border-radius: 3px;',
  'color: #ff0000;'
);

console.log(
  '%c SUCCESS %c Utilisateur créé',
  'background: #00aa00; color: white; padding: 2px 6px; border-radius: 3px;',
  'color: #00aa00;'
);

// Styles multiples
console.log(
  '%cVariable:%c user.name %c=%c "Jean"',
  'color: gray;',
  'color: purple;',
  'color: gray;',
  'color: green;'
);

Breakpoints Conditionnels

Les breakpoints sont plus puissants que de simples pauses dans le code.

Breakpoints avec Condition

Dans Chrome DevTools, cliquez droit sur un breakpoint et sélectionnez "Edit breakpoint" :

// Code original
async function processOrders(orders) {
  for (const order of orders) {
    await processOrder(order);
  }
}

// Condition dans le breakpoint : order.total > 1000
// Ne pause que quand order.total est supérieur à 1000

Logpoints (Breakpoints qui ne pausent pas)

Ajoutez des logs sans modifier le code :

// Dans DevTools, ajoutez un logpoint avec :
// "Traitement order:", order.id, "total:", order.total

// Équivalent à ajouter console.log() dans le code,
// mais sans avoir besoin de modifier et recharger

DOM Breakpoints

Pausez quand le DOM change :

  1. Inspectez l'élément dans le panneau Elements
  2. Cliquez droit → Break on
  3. Choisissez : subtree modifications, attribute modifications, ou node removal
// Utile pour trouver le code qui modifie des éléments de façon inattendue
// DevTools va pauser et montrer exactement quel code a fait la modification

Debugging de Code Asynchrone

Le code asynchrone peut être difficile à debugger. Voici des techniques spécifiques :

Async Stack Traces

Chrome DevTools montre le stack trace complet incluant les appels asynchrones :

async function fetchUserData(userId) {
  // Breakpoint ici montre tout le chemin asynchrone
  const response = await fetch(`/api/users/${userId}`);
  const data = await response.json();
  return data;
}

async function loadDashboard() {
  const user = await fetchUserData(currentUserId);
  await loadUserPosts(user.id);
  await loadUserNotifications(user.id);
}

// Dans le stack trace vous verrez :
// - fetchUserData (async)
// - loadDashboard (async)
// - buttonClickHandler
// - (click event)

Promise Rejections

// Activez "Pause on caught exceptions" pour debugger les promises rejetées

async function riskyOperation() {
  try {
    const result = await mightFail();
    return result;
  } catch (error) {
    // Avec "Pause on caught exceptions" actif,
    // le debugger s'arrête ici avant que le catch s'exécute
    console.error('Opération échouée:', error);
    throw error;
  }
}

Event Listener Breakpoints

Dans le panneau Sources → Event Listener Breakpoints :

// Cochez des événements spécifiques pour pauser :
// - Mouse → click
// - Keyboard → keydown
// - XHR/Fetch → Any XHR or fetch

// Utile pour comprendre le flux d'événements
// sans avoir besoin d'ajouter des breakpoints manuellement

Performance Profiling

Identifier les goulots d'étranglement de performance est essentiel pour des applications réactives.

Performance Panel

// Démarrez un enregistrement dans le panneau Performance
// Exécutez l'action qui est lente
// Arrêtez l'enregistrement et analysez

// Ce qu'il faut chercher :
// - Long Tasks (tâches > 50ms)
// - Layout Thrashing (beaucoup de reflows)
// - Scripting time élevé
// - Blocking time

Identifier le Layout Thrashing

// ❌ MAUVAIS - Cause plusieurs reflows
function updateElements(elements) {
  elements.forEach(el => {
    const height = el.offsetHeight; // Force reflow (lecture)
    el.style.height = height + 10 + 'px'; // Force reflow (écriture)
  });
}

// ✅ BON - Groupe lectures et écritures
function updateElementsOptimized(elements) {
  // D'abord, toutes les lectures
  const heights = elements.map(el => el.offsetHeight);

  // Ensuite, toutes les écritures
  elements.forEach((el, i) => {
    el.style.height = heights[i] + 10 + 'px';
  });
}

Memory Profiling

// Dans le panneau Memory, prenez des snapshots pour identifier les memory leaks

// Pattern courant de memory leak :
class Component {
  constructor() {
    // Event listener qui n'est jamais supprimé
    window.addEventListener('resize', this.handleResize);
  }

  handleResize = () => {
    // ...
  };

  // ❌ Sans cleanup, le listener maintient une référence au composant
}

// ✅ Correction :
class ComponentFixed {
  constructor() {
    window.addEventListener('resize', this.handleResize);
  }

  handleResize = () => {
    // ...
  };

  destroy() {
    window.removeEventListener('resize', this.handleResize);
  }
}

Source Maps et Debugging de Code Minifié

Configuration des Source Maps

// webpack.config.js
module.exports = {
  devtool: 'source-map', // Production
  // ou
  devtool: 'eval-source-map', // Développement (plus rapide)
};

// vite.config.js
export default {
  build: {
    sourcemap: true,
  },
};

Debugging avec Source Maps

Avec les source maps configurés, vous pouvez :

  1. Voir le code original dans le panneau Sources
  2. Ajouter des breakpoints dans le code TypeScript/ES6+
  3. Voir les variables avec leurs noms originaux
  4. Les stack traces pointent vers les fichiers originaux
// Code TypeScript original
interface User {
  id: number;
  name: string;
}

async function getUser(id: number): Promise<User> {
  // Breakpoint ici fonctionne normalement
  const response = await fetch(`/api/users/${id}`);
  return response.json();
}

Snippets et Overrides

Snippets Réutilisables

Créez des snippets dans DevTools pour des tâches répétitives :

// Snippet : Vider localStorage et recharger
(function clearAndReload() {
  localStorage.clear();
  sessionStorage.clear();
  location.reload();
})();

// Snippet : Exporter les données de la console
(function exportConsoleData() {
  const data = /* vos données */;
  const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = 'export.json';
  a.click();
})();

// Snippet : Surveiller tous les appels fetch
(function monitorFetch() {
  const originalFetch = window.fetch;
  window.fetch = async (...args) => {
    console.group('Fetch Request');
    console.log('URL:', args[0]);
    console.log('Options:', args[1]);

    const start = performance.now();
    const response = await originalFetch(...args);
    const duration = performance.now() - start;

    console.log('Status:', response.status);
    console.log('Duration:', duration.toFixed(2) + 'ms');
    console.groupEnd();

    return response;
  };
  console.log('Surveillance fetch activée');
})();

Local Overrides

Modifiez des fichiers de production localement :

  1. Dans le panneau Sources, créez un dossier overrides
  2. Sélectionnez un fichier et éditez
  3. Les changements persistent entre les rechargements
  4. Utile pour tester des fixes en production sans deploy

Network Debugging

Filtres Avancés

// Dans le panneau Network, utilisez des filtres :

// Par type
// is:running - Requêtes en cours
// larger-than:1M - Plus grandes que 1MB
// status-code:500 - Seulement status 500

// Par domaine
// domain:api.example.com

// Expressions négatives
// -domain:analytics.com - Exclure domaine

// Filtres multiples
// method:POST status-code:400

Throttling et Offline

// Simuler des connexions lentes
// Network panel → No throttling → Sélectionnez :
// - Slow 3G
// - Fast 3G
// - Offline

// Ou créez des profils personnalisés :
// - Download: 100kb/s
// - Upload: 50kb/s
// - Latency: 500ms

Conclusion

Maîtriser les outils de debugging transforme votre productivité en tant que développeur. Au lieu de deviner où est le problème, vous pouvez aller directement à la cause racine.

Quelques bonnes pratiques recommandées :

  • Utilisez des breakpoints conditionnels au lieu de multiples console.log()
  • Apprenez les raccourcis clavier de DevTools
  • Utilisez les logpoints quand vous ne voulez pas modifier le code
  • Faites du profiling régulièrement pour identifier les problèmes de performance avant qu'ils ne deviennent critiques

Les outils sont disponibles gratuitement dans tous les navigateurs modernes. Ça vaut la peine d'investir du temps pour les maîtriser.

Si vous voulez continuer à approfondir vos connaissances en JavaScript, je recommande de consulter l'article Web APIs Modernes du Navigateur avec JavaScript en 2025 où nous explorons des APIs puissantes qui peuvent améliorer vos applications.

C'est parti ! 🦅

Commentaires (0)

Cet article n'a pas encore de commentaires. Soyez le premier!

Ajouter des commentaires