Cours JavaScript et React · Module 1: Bases de React
Le Virtual DOM et ses avantages
Dans cette leçon6
L'une des fonctionnalités les plus innovantes de React est le concept de Virtual DOM (Document Object Model). Pour comprendre son importance, nous devons d'abord comprendre le problème qu'il résout.
Le problème du DOM traditionnel
Imagine le DOM du navigateur comme une immense carte stellaire, où chaque étoile, planète et station spatiale doit être placée avec précision. Chaque modification de cette carte a un coût : le navigateur doit recalculer la position des objets concernés par le changement.
Dans l'approche traditionnelle, lorsqu'un utilisateur effectue une action (par exemple, clique sur un bouton), c'est à toi de :
- Trouver dans le DOM tous les éléments concernés par le changement
- Modifier chacun d'eux à la main - ou, par facilité, reconstruire toute la section de la page (par exemple avec innerHTML)
Après chaque modification, le navigateur recalcule les styles et la mise en page (layout) et repeint la partie modifiée de la page. Si tu reconstruis toute une section, il le fait pour tous ses éléments, même si un seul a changé.
C'est comme reconcevoir tout le vaisseau spatial alors que tu veux simplement changer la couleur d'un seul bouton du tableau de bord !
Comment fonctionne le Virtual DOM
React a introduit une approche révolutionnaire :
- L'utilisateur effectue une action
- React crée un nouveau Virtual DOM (une copie JavaScript légère du DOM réel)
- React compare le Virtual DOM précédent avec le nouveau (l'algorithme de « diffing »)
- React met à jour uniquement les éléments du DOM réel qui ont réellement changé
C'est comme disposer d'une maquette holographique du vaisseau spatial, où tu planifies les changements et les testes avant de les appliquer au vrai vaisseau. Ce processus est incroyablement efficace, surtout dans les applications complexes où de nombreux éléments peuvent changer simultanément.
Le Virtual DOM en action - exemple
Prenons un exemple simple : nous avons une liste de tâches, et un utilisateur vient de marquer une tâche comme terminée.
Dans l'approche traditionnelle :
- Soit tu trouves et modifies toi-même le bon élément de la liste - et tu dois penser à chaque endroit de ce type dans ton code
- Soit tu reconstruis toute la liste (par exemple avec innerHTML) - plus simple, mais le navigateur traite alors tous les éléments de la liste
Avec React et le Virtual DOM :
- React crée une nouvelle version du Virtual DOM avec l'état mis à jour de la tâche
- Il la compare avec la version précédente
- Il trouve un seul élément modifié (la tâche marquée comme terminée)
- Il met à jour uniquement cet élément précis dans le DOM réel
Détails d'implémentation du Virtual DOM
Le Virtual DOM fonctionne à plusieurs niveaux :
- Représentation des éléments - Chaque élément JSX est transformé en un objet JavaScript léger représentant un nœud du DOM
- Réconciliation - Le processus de comparaison de deux arbres Virtual DOM et de calcul de l'ensemble minimal d'opérations nécessaires pour mettre à jour le DOM réel
- Regroupement (Batching) - React regroupe plusieurs mises à jour en une seule session afin de minimiser le nombre d'opérations sur le DOM réel
1// Ce code JSX
2<div className="task-list">
3 <Task completed={true} name="Task 1" />
4 <Task completed={false} name="Task 2" />
5</div>
6
7// Est transformé en une structure semblable à celle-ci (simplifiée)
8{
9 type: 'div',
10 props: {
11 className: 'task-list',
12 children: [
13 {
14 type: Task,
15 props: { completed: true, name: 'Task 1' }
16 },
17 {
18 type: Task,
19 props: { completed: false, name: 'Task 2' }
20 }
21 ]
22 }
23}Avantages du Virtual DOM
Les avantages de cette approche sont considérables :
- Performance - surtout dans les applications complexes où l'approche traditionnelle pourrait provoquer des ralentissements perceptibles
- Abstraction du DOM - les développeurs peuvent se concentrer sur la logique de l'application plutôt que sur les détails de manipulation du DOM
- Prévisibilité - il est plus facile de suivre les changements d'état et de déboguer les problèmes
- Facilité de test - les composants peuvent être testés de façon isolée
- Multiplateforme - puisque le Virtual DOM est un simple objet JavaScript, le même concept peut être utilisé sur différentes plateformes (Web, mobile avec React Native)
Fiber - la nouvelle génération du Virtual DOM
Depuis React 16, une nouvelle implémentation de l'algorithme de réconciliation appelée Fiber a été introduite. Cette architecture permet :
- Des mises à jour interruptibles - les longues opérations de rendu peuvent être découpées en plus petites parties
- La priorisation des mises à jour - certaines mises à jour (par exemple les animations) peuvent avoir une priorité plus élevée
- Une meilleure gestion des erreurs
- Une meilleure prise en charge du mode concurrent (Concurrent Mode)
Cette évolution du Virtual DOM montre comment React fait constamment évoluer ses technologies clés pour relever les défis des applications web modernes.
Code de cette leçon : App.jsx
1import React from 'react';
2
3// Composant démontrant le DOM traditionnel
4function TraditionalDOMDemo() {
5 const [updates, setUpdates] = React.useState(0);
6 const [isAnimating, setIsAnimating] = React.useState(false);
7
8 const simulateTraditionalUpdate = () => {
9 setIsAnimating(true);
10 setUpdates(prev => prev + 1);
11
12 setTimeout(() => {
13 setIsAnimating(false);
14 }, 1000);
15 };
16
17 return (
18 <div style={{
19 backgroundColor: '#ffebee',
20 padding: '20px',
21 borderRadius: '8px',
22 border: '2px solid #f44336',
23 margin: '15px 0'
24 }}>
25 <h3 style={{ color: '#c62828', marginBottom: '15px' }}>
26 DOM traditionnel (lent)
27 </h3>
28
29 <div style={{
30 display: 'flex',
31 alignItems: 'center',
32 marginBottom: '15px',
33 gap: '15px'
34 }}>
35 <button
36 onClick={simulateTraditionalUpdate}
37 disabled={isAnimating}
38 style={{
39 padding: '10px 20px',
40 backgroundColor: isAnimating ? '#ccc' : '#f44336',
41 color: 'white',
42 border: 'none',
43 borderRadius: '4px',
44 cursor: isAnimating ? 'not-allowed' : 'pointer'
45 }}
46 >
47 {isAnimating ? 'Mise à jour...' : 'Mettre à jour le DOM'}
48 </button>
49
50 <div style={{
51 padding: '8px 15px',
52 backgroundColor: 'white',
53 borderRadius: '4px',
54 border: '1px solid #ccc'
55 }}>
56 Mises à jour : {updates}
57 </div>
58 </div>
59
60 <div style={{
61 backgroundColor: 'white',
62 padding: '15px',
63 borderRadius: '4px',
64 opacity: isAnimating ? 0.5 : 1,
65 transition: 'opacity 1s'
66 }}>
67 <h4>Étapes de mise à jour traditionnelle :</h4>
68 <ol style={{ color: '#666' }}>
69 <li>Trouver tous les éléments à modifier</li>
70 <li>Modifier chaque élément individuellement</li>
71 <li>Recalculer les styles et la mise en page (reflow)</li>
72 <li>Repeindre la partie modifiée de la page (repaint)</li>
73 </ol>
74 {isAnimating && (
75 <div style={{
76 color: '#f44336',
77 fontStyle: 'italic',
78 marginTop: '10px'
79 }}>
80 ⏳ Mise à jour... Cela peut prendre un moment sur les grandes pages !
81 </div>
82 )}
83 </div>
84 </div>
85 );
86}
87
88// Composant démontrant le Virtual DOM
89function VirtualDOMDemo() {
90 const [updates, setUpdates] = React.useState(0);
91 const [isProcessing, setIsProcessing] = React.useState(false);
92 const [virtualChanges, setVirtualChanges] = React.useState([]);
93
94 const simulateVirtualDOMUpdate = () => {
95 setIsProcessing(true);
96 setVirtualChanges(['Création du Virtual DOM...']);
97
98 setTimeout(() => {
99 setVirtualChanges(['Création du Virtual DOM...', 'Comparaison avec la version précédente...']);
100 }, 300);
101
102 setTimeout(() => {
103 setVirtualChanges([
104 'Création du Virtual DOM...',
105 'Comparaison avec la version précédente...',
106 '1 changement trouvé à appliquer'
107 ]);
108 }, 600);
109
110 setTimeout(() => {
111 setVirtualChanges([
112 'Création du Virtual DOM...',
113 'Comparaison avec la version précédente...',
114 '1 changement trouvé à appliquer',
115 'Mise à jour des seuls éléments nécessaires'
116 ]);
117 setUpdates(prev => prev + 1);
118 setIsProcessing(false);
119 }, 900);
120
121 setTimeout(() => {
122 setVirtualChanges([]);
123 }, 2000);
124 };
125
126 return (
127 <div style={{
128 backgroundColor: '#e8f5e8',
129 padding: '20px',
130 borderRadius: '8px',
131 border: '2px solid #4caf50',
132 margin: '15px 0'
133 }}>
134 <h3 style={{ color: '#2e7d32', marginBottom: '15px' }}>
135 Virtual DOM de React (rapide)
136 </h3>
137
138 <div style={{
139 display: 'flex',
140 alignItems: 'center',
141 marginBottom: '15px',
142 gap: '15px'
143 }}>
144 <button
145 onClick={simulateVirtualDOMUpdate}
146 disabled={isProcessing}
147 style={{
148 padding: '10px 20px',
149 backgroundColor: isProcessing ? '#ccc' : '#4caf50',
150 color: 'white',
151 border: 'none',
152 borderRadius: '4px',
153 cursor: isProcessing ? 'not-allowed' : 'pointer'
154 }}
155 >
156 {isProcessing ? 'Traitement...' : 'Mettre à jour le Virtual DOM'}
157 </button>
158
159 <div style={{
160 padding: '8px 15px',
161 backgroundColor: 'white',
162 borderRadius: '4px',
163 border: '1px solid #ccc'
164 }}>
165 Mises à jour : {updates}
166 </div>
167 </div>
168
169 <div style={{
170 backgroundColor: 'white',
171 padding: '15px',
172 borderRadius: '4px'
173 }}>
174 <h4>Processus du Virtual DOM :</h4>
175 <ul style={{ color: '#666', minHeight: '100px' }}>
176 {virtualChanges.map((change, index) => (
177 <li key={index} style={{
178 marginBottom: '5px',
179 opacity: 0,
180 animation: 'fadeIn 0.5s forwards',
181 animationDelay: `${index * 0.2}s`
182 }}>
183 {change}
184 </li>
185 ))}
186 {!isProcessing && virtualChanges.length === 0 && (
187 <li style={{ color: '#999', fontStyle: 'italic' }}>
188 Clique sur le bouton pour voir le processus du Virtual DOM en action
189 </li>
190 )}
191 </ul>
192 </div>
193
194 <style>
195 {`
196 @keyframes fadeIn {
197 from { opacity: 0; }
198 to { opacity: 1; }
199 }
200 `}
201 </style>
202 </div>
203 );
204}
205
206// Composant démontrant les différences de performance
207function PerformanceDifference() {
208 const [itemCount, setItemCount] = React.useState(100);
209 const [items, setItems] = React.useState([]);
210
211 React.useEffect(() => {
212 const newItems = Array.from({ length: itemCount }, (_, i) => ({
213 id: i,
214 name: `Élément ${i + 1}`,
215 value: Math.floor(Math.random() * 100)
216 }));
217 setItems(newItems);
218 }, [itemCount]);
219
220 const updateAllItems = () => {
221 setItems(prevItems =>
222 prevItems.map(item => ({
223 ...item,
224 value: Math.floor(Math.random() * 100)
225 }))
226 );
227 };
228
229 return (
230 <div style={{
231 backgroundColor: '#f3e5f5',
232 padding: '20px',
233 borderRadius: '8px',
234 border: '2px solid #9c27b0',
235 margin: '15px 0'
236 }}>
237 <h3 style={{ color: '#7b1fa2', marginBottom: '15px' }}>
238 Démonstration des performances du Virtual DOM
239 </h3>
240
241 <div style={{
242 display: 'flex',
243 gap: '15px',
244 marginBottom: '15px',
245 flexWrap: 'wrap',
246 alignItems: 'center'
247 }}>
248 <label style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
249 Nombre d'éléments :
250 <input
251 type="range"
252 min="50"
253 max="1000"
254 step="50"
255 value={itemCount}
256 onChange={(e) => setItemCount(Number(e.target.value))}
257 style={{ width: '150px' }}
258 />
259 <span style={{ minWidth: '60px' }}>{itemCount}</span>
260 </label>
261
262 <button
263 onClick={updateAllItems}
264 style={{
265 padding: '10px 20px',
266 backgroundColor: '#9c27b0',
267 color: 'white',
268 border: 'none',
269 borderRadius: '4px',
270 cursor: 'pointer'
271 }}
272 >
273 Tout mettre à jour
274 </button>
275 </div>
276
277 <div style={{
278 display: 'grid',
279 gridTemplateColumns: 'repeat(auto-fill, minmax(120px, 1fr))',
280 gap: '8px',
281 maxHeight: '200px',
282 overflow: 'auto',
283 backgroundColor: 'white',
284 padding: '15px',
285 borderRadius: '4px'
286 }}>
287 {items.map(item => (
288 <div
289 key={item.id}
290 style={{
291 padding: '8px',
292 backgroundColor: `hsl(${item.value * 3.6}, 70%, 90%)`,
293 borderRadius: '4px',
294 textAlign: 'center',
295 fontSize: '12px',
296 transition: 'background-color 0.3s'
297 }}
298 >
299 <div style={{ fontWeight: 'bold' }}>{item.name}</div>
300 <div>{item.value}</div>
301 </div>
302 ))}
303 </div>
304
305 <div style={{
306 marginTop: '15px',
307 padding: '10px',
308 backgroundColor: 'white',
309 borderRadius: '4px',
310 fontSize: '14px',
311 color: '#666'
312 }}>
313 <strong>Remarque :</strong> Même en mettant à jour {itemCount} éléments,
314 React, grâce au Virtual DOM, traite les changements en douceur et ne met à jour que
315 les éléments qui ont réellement changé !
316 </div>
317 </div>
318 );
319}
320
321// Composant avec visualisation interactive du processus
322function VirtualDOMVisualization() {
323 const [step, setStep] = React.useState(0);
324 const steps = [
325 { title: "État initial", desc: "Nous avons un Virtual DOM existant" },
326 { title: "Changement d'état", desc: "L'utilisateur effectue une action (par exemple, clique sur un bouton)" },
327 { title: "Nouveau Virtual DOM", desc: "React crée un nouveau Virtual DOM" },
328 { title: "Algorithme de diff", desc: "React compare l'ancien et le nouveau Virtual DOM" },
329 { title: "Changements minimaux", desc: "React identifie l'ensemble minimal de changements" },
330 { title: "Mise à jour du DOM", desc: "Seuls les éléments nécessaires sont mis à jour" }
331 ];
332
333 const nextStep = () => {
334 setStep(prev => (prev + 1) % steps.length);
335 };
336
337 const resetSteps = () => {
338 setStep(0);
339 };
340
341 return (
342 <div style={{
343 backgroundColor: '#fff3e0',
344 padding: '20px',
345 borderRadius: '8px',
346 border: '2px solid #ff9800',
347 margin: '15px 0'
348 }}>
349 <h3 style={{ color: '#f57c00', marginBottom: '15px' }}>
350 Pas à pas : comment fonctionne le Virtual DOM
351 </h3>
352
353 <div style={{
354 display: 'flex',
355 gap: '10px',
356 marginBottom: '20px'
357 }}>
358 <button onClick={nextStep} style={{
359 padding: '10px 20px',
360 backgroundColor: '#ff9800',
361 color: 'white',
362 border: 'none',
363 borderRadius: '4px',
364 cursor: 'pointer'
365 }}>
366 Étape suivante
367 </button>
368
369 <button onClick={resetSteps} style={{
370 padding: '10px 20px',
371 backgroundColor: '#666',
372 color: 'white',
373 border: 'none',
374 borderRadius: '4px',
375 cursor: 'pointer'
376 }}>
377 Reset
378 </button>
379 </div>
380
381 <div style={{
382 backgroundColor: 'white',
383 padding: '20px',
384 borderRadius: '8px',
385 minHeight: '120px'
386 }}>
387 <div style={{
388 display: 'flex',
389 alignItems: 'center',
390 marginBottom: '15px'
391 }}>
392 <div style={{
393 width: '40px',
394 height: '40px',
395 backgroundColor: '#ff9800',
396 color: 'white',
397 borderRadius: '50%',
398 display: 'flex',
399 alignItems: 'center',
400 justifyContent: 'center',
401 fontWeight: 'bold',
402 marginRight: '15px'
403 }}>
404 {step + 1}
405 </div>
406 <h4 style={{ margin: 0, color: '#333' }}>
407 {steps[step].title}
408 </h4>
409 </div>
410
411 <p style={{ color: '#666', fontSize: '16px', lineHeight: '1.5' }}>
412 {steps[step].desc}
413 </p>
414
415 <div style={{
416 marginTop: '15px',
417 display: 'flex',
418 gap: '5px'
419 }}>
420 {steps.map((_, index) => (
421 <div
422 key={index}
423 style={{
424 width: '12px',
425 height: '12px',
426 borderRadius: '50%',
427 backgroundColor: index <= step ? '#ff9800' : '#ddd',
428 transition: 'background-color 0.3s'
429 }}
430 />
431 ))}
432 </div>
433 </div>
434 </div>
435 );
436}
437
438export default function App() {
439 return (
440 <div style={{
441 fontFamily: 'Arial, sans-serif',
442 maxWidth: '900px',
443 margin: '0 auto',
444 padding: '20px'
445 }}>
446 <header style={{
447 textAlign: 'center',
448 marginBottom: '30px',
449 padding: '25px',
450 background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
451 color: 'white',
452 borderRadius: '12px'
453 }}>
454 <h1 style={{ margin: '0 0 10px 0', fontSize: '2.2rem' }}>
455 Le Virtual DOM et ses avantages
456 </h1>
457 <p style={{ margin: 0, fontSize: '1rem', opacity: 0.9 }}>
458 Découvre le secret de la performance de React et comprends pourquoi le Virtual DOM
459 est une véritable révolution dans le monde du front-end
460 </p>
461 </header>
462
463 <section style={{ marginBottom: '30px' }}>
464 <h2 style={{
465 color: '#333',
466 borderBottom: '2px solid #667eea',
467 paddingBottom: '10px',
468 marginBottom: '20px'
469 }}>
470 DOM traditionnel vs Virtual DOM
471 </h2>
472
473 <TraditionalDOMDemo />
474 <VirtualDOMDemo />
475 </section>
476
477 <VirtualDOMVisualization />
478 <PerformanceDifference />
479
480 <div style={{
481 backgroundColor: '#e3f2fd',
482 padding: '25px',
483 borderRadius: '12px',
484 marginTop: '30px'
485 }}>
486 <h3 style={{ color: '#1976d2', textAlign: 'center', marginBottom: '20px' }}>
487 Avantages clés du Virtual DOM
488 </h3>
489
490 <div style={{
491 display: 'grid',
492 gridTemplateColumns: 'repeat(auto-fit, minmax(250px, 1fr))',
493 gap: '20px'
494 }}>
495 <div style={{ textAlign: 'center' }}>
496 <div style={{ fontSize: '40px', marginBottom: '10px' }}>↯</div>
497 <h4 style={{ color: '#1976d2' }}>Performance</h4>
498 <p style={{ margin: 0, color: '#666' }}>
499 Mises à jour minimales du DOM = applications plus rapides
500 </p>
501 </div>
502
503 <div style={{ textAlign: 'center' }}>
504 <div style={{ fontSize: '40px', marginBottom: '10px' }}>◆</div>
505 <h4 style={{ color: '#1976d2' }}>Prévisibilité</h4>
506 <p style={{ margin: 0, color: '#666' }}>
507 Suivi des changements et débogage plus faciles
508 </p>
509 </div>
510
511 <div style={{ textAlign: 'center' }}>
512 <div style={{ fontSize: '40px', marginBottom: '10px' }}>◆</div>
513 <h4 style={{ color: '#1976d2' }}>Testabilité</h4>
514 <p style={{ margin: 0, color: '#666' }}>
515 Les composants peuvent être testés de façon isolée
516 </p>
517 </div>
518
519 <div style={{ textAlign: 'center' }}>
520 <div style={{ fontSize: '40px', marginBottom: '10px' }}>●</div>
521 <h4 style={{ color: '#1976d2' }}>Multiplateforme</h4>
522 <p style={{ margin: 0, color: '#666' }}>
523 Le même concept fonctionne partout
524 </p>
525 </div>
526 </div>
527 </div>
528 </div>
529 );
530}Tu as repéré une erreur dans cette leçon ?
Teste tes connaissances
Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.
1. Qu'est-ce que le Virtual DOM dans React ?
2. Que sont les composants dans React ?
3. Quel est le principal avantage du Virtual DOM dans React ?
4. Que fait l'algorithme de diffing (réconciliation) dans React ?
Tâches pratiques dans le jeu
- Clique dans l'ordre
Ordonne le processus du Virtual DOM dans l'ordre :