Cours JavaScript et TypeScript · Module 5: JavaScript avancé
L'import dynamique de modules
Dans cette leçon7
Dans les exercices précédents, nous avons découvert les deux principaux systèmes de modules JavaScript : les modules ES et CommonJS. Les deux permettent d'organiser le code en modules indépendants, mais nous avons surtout manipulé des imports statiques. Passons maintenant au niveau supérieur - l'import dynamique de modules, qui nous offre une flexibilité bien plus grande.
Pour notre système de gestion de Jurassic Park, les imports dynamiques peuvent être cruciaux. Imagine avoir besoin de procédures d'urgence différentes selon ce qui se passe - une évasion de T-Rex demande un ensemble d'actions différent d'une panne de courant ou d'un ouragan qui approche !
Introduction à l'import dynamique
L'import dynamique de modules permet de charger les modules pendant l'exécution du programme, plutôt que lors de son initialisation. Cela signifie qu'on peut charger un module exactement quand on en a besoin, ce qui économise de la mémoire et du temps de chargement pour des modules qui ne seront peut-être jamais utilisés.
Syntaxe de l'import dynamique
L'import dynamique utilise la fonction import(), qui renvoie une Promise contenant le module chargé :
1// Import statique (toujours chargé)
2import { DinosaurTracker } from './dinosaurTracker.js';
3
4// Import dynamique (chargé à la demande)
5import('./emergencyProcedures.js')
6 .then(module => {
7 // On peut maintenant utiliser le module chargé
8 module.initializeEmergencyProtocol();
9 })
10 .catch(error => {
11 console.error("Échec du chargement des procédures d'urgence :", error);
12 });Il est encore plus pratique d'utiliser les imports dynamiques avec async/await :
1async function handleEmergency(type) {
2 try {
3 // Importe dynamiquement le bon module de procédure d'urgence
4 const emergencyModule = await import(`./emergencies/${type}.js`);
5
6 // Utilise le module chargé
7 return emergencyModule.executeProtocol();
8 } catch (error) {
9 console.error(`Échec du chargement de la procédure : ${type}`, error);
10 // Repli sur la procédure d'urgence générale
11 const fallbackModule = await import('./emergencies/general.js');
12 return fallbackModule.executeProtocol();
13 }
14}
15
16// Utilise notre fonction
17handleEmergency('dinosaurEscape').then(result => {
18 console.log("État de la procédure d'urgence :", result);
19});Applications de l'import dynamique dans Jurassic Park
Réfléchissons aux applications pratiques de l'import dynamique dans notre système de gestion de Jurassic Park :
1. Chargement à la demande
On peut charger des fonctionnalités uniquement quand on en a besoin, ce qui est particulièrement utile pour des modules volumineux mais rarement utilisés :
1// dashboardController.js
2async function loadDashboard(dashboardType) {
3 // Le tableau de bord de base est toujours chargé
4 const statusPanel = document.getElementById('statusPanel');
5 statusPanel.innerHTML = 'Chargement...';
6
7 try {
8 // Importe dynamiquement le type de tableau de bord demandé
9 const dashboardModule = await import(`./dashboards/${dashboardType}Dashboard.js`);
10
11 // Initialise le tableau de bord
12 const dashboard = new dashboardModule.default();
13 await dashboard.initialize(statusPanel);
14
15 console.log(`Tableau de bord ${dashboardType} chargé avec succès`);
16 } catch (error) {
17 console.error(`Erreur de chargement du tableau de bord ${dashboardType} : `, error);
18 statusPanel.innerHTML = `Erreur de chargement du tableau de bord : ${error.message}`;
19 }
20}
21
22// Boutons de l'interface
23document.getElementById('btnDinosaurs').addEventListener('click', () => {
24 loadDashboard('dinosaurs');
25});
26
27document.getElementById('btnSecurity').addEventListener('click', () => {
28 loadDashboard('security');
29});
30
31document.getElementById('btnVisitors').addEventListener('click', () => {
32 loadDashboard('visitors');
33});2. Différentes implémentations selon l'environnement
On peut charger dynamiquement différentes implémentations selon l'environnement (production, développement, test) :
1async function initializeTracking() {
2 const environment = process.env.NODE_ENV || 'development';
3
4 try {
5 // Charge l'implémentation adaptée à l'environnement
6 const trackingModule = await import(`./tracking/${environment}.js`);
7
8 // Initialise le système de suivi
9 const tracker = trackingModule.createTracker();
10 tracker.initialize();
11
12 return tracker;
13 } catch (error) {
14 console.error(`Échec de l'initialisation du suivi pour l'environnement ${environment} : `, error);
15 // Repli sur la version de développement
16 const fallbackModule = await import('./tracking/development.js');
17 return fallbackModule.createTracker();
18 }
19}
20
21// Utilisation de la fonction
22initializeTracking().then(tracker => {
23 tracker.trackEvent('app_initialized', { timestamp: Date.now() });
24});3. Chargement paresseux des composants d'interface
Dans une application frontend, on peut différer le chargement des composants d'interface pour accélérer le chargement initial de la page :
1// dynamicComponents.js
2export async function loadComponent(componentName) {
3 try {
4 const module = await import(`./components/${componentName}.js`);
5 return module.default;
6 } catch (error) {
7 console.error(`Échec du chargement du composant ${componentName} : `, error);
8 throw error;
9 }
10}
11
12// Dans le fichier principal de l'application
13import { loadComponent } from './dynamicComponents.js';
14
15document.getElementById('btnShowMap').addEventListener('click', async () => {
16 try {
17 // Charge la carte du parc uniquement quand l'utilisateur en a besoin
18 const ParkMap = await loadComponent('ParkMap');
19 const mapContainer = document.getElementById('mapContainer');
20
21 const parkMap = new ParkMap({
22 container: mapContainer,
23 initialZoom: 2,
24 centerCoordinates: [34.5, 12.8]
25 });
26
27 parkMap.render();
28 } catch (error) {
29 alert(`Échec du chargement de la carte : ${error.message}`);
30 }
31});4. Chargement dynamique des procédures et protocoles de sécurité
L'une des applications les plus importantes dans notre parc sera le système de procédures de sécurité :
1// securitySystem.js
2export class SecuritySystem {
3 constructor() {
4 this.activeProtocols = new Map();
5 this.alertLevel = 'green';
6 }
7
8 async activateProtocol(protocolName, params = {}) {
9 try {
10 // Vérifie si le protocole est déjà actif
11 if (this.activeProtocols.has(protocolName)) {
12 console.log(`Le protocole ${protocolName} est déjà actif`);
13 return this.activeProtocols.get(protocolName);
14 }
15
16 // Importe dynamiquement le protocole
17 const protocolModule = await import(`./protocols/${protocolName}.js`);
18
19 // Initialise le protocole
20 const protocol = new protocolModule.default(params);
21 await protocol.initialize();
22
23 // Ajoute aux protocoles actifs
24 this.activeProtocols.set(protocolName, protocol);
25
26 console.log(`Protocole ${protocolName} activé avec succès`);
27 return protocol;
28 } catch (error) {
29 console.error(`Erreur lors de l'activation du protocole ${protocolName} : `, error);
30 throw new Error(`Échec de l'activation du protocole ${protocolName} : ${error.message}`);
31 }
32 }
33
34 async deactivateProtocol(protocolName) {
35 const protocol = this.activeProtocols.get(protocolName);
36 if (!protocol) {
37 console.log(`Le protocole ${protocolName} n'est pas actif`);
38 return false;
39 }
40
41 try {
42 await protocol.shutdown();
43 this.activeProtocols.delete(protocolName);
44 console.log(`Protocole ${protocolName} désactivé avec succès`);
45 return true;
46 } catch (error) {
47 console.error(`Erreur lors de la désactivation du protocole ${protocolName} : `, error);
48 return false;
49 }
50 }
51
52 async handleEmergency(type, location) {
53 console.log(`Gestion de l'urgence : ${type} à l'emplacement ${location}`);
54
55 // Met à jour le niveau d'alerte
56 this.alertLevel = 'red';
57
58 try {
59 // Importe la procédure d'urgence appropriée
60 const emergencyModule = await import(`./emergencies/${type}.js`);
61
62 // Exécute la procédure
63 const procedure = new emergencyModule.EmergencyProcedure(location);
64 const emergencyId = await procedure.execute();
65
66 console.log(`Procédure d'urgence pour ${type} lancée, ID : ${emergencyId}`);
67 return emergencyId;
68 } catch (error) {
69 console.error(`Échec de la gestion de l'urgence ${type} : `, error);
70
71 // Repli sur la procédure d'urgence générale
72 try {
73 const generalModule = await import('./emergencies/general.js');
74 const procedure = new generalModule.EmergencyProcedure(location, { originalType: type });
75 return procedure.execute();
76 } catch (fallbackError) {
77 console.error(`Erreur critique ! Même la procédure générale a échoué : `, fallbackError);
78 return null;
79 }
80 }
81 }
82}
83
84// Utilisation du système de sécurité
85const security = new SecuritySystem();
86
87// Exemple d'activation de protocole
88document.getElementById('btnLockdown').addEventListener('click', async () => {
89 try {
90 const protocol = await security.activateProtocol('lockdown', {
91 sectors: ['A1', 'B2'],
92 reason: 'Évasion de dinosaure'
93 });
94
95 document.getElementById('status').textContent = `Protocole de confinement actif dans ${protocol.affectedSectors.length} secteurs`;
96 } catch (error) {
97 alert(`Erreur d'activation du protocole : ${error.message}`);
98 }
99});
100
101// Exemple de signalement d'urgence
102async function reportEmergency(type, location) {
103 try {
104 const emergencyId = await security.handleEmergency(type, location);
105 return emergencyId;
106 } catch (error) {
107 console.error(`Échec du signalement de l'urgence : `, error);
108 return null;
109 }
110}Exemples d'implémentation de modules
Voyons à quoi pourraient ressembler certains des modules importés dynamiquement dans notre système :
Exemple de protocole de sécurité (lockdown.js)
1// protocols/lockdown.js
2export default class LockdownProtocol {
3 constructor(params) {
4 this.sectors = params.sectors || [];
5 this.reason = params.reason || 'Raison inconnue';
6 this.timestamp = new Date();
7 this.active = false;
8 this.affectedSectors = [];
9 }
10
11 async initialize() {
12 console.log(`Initialisation du protocole de confinement pour les secteurs : ${this.sectors.join(', ')}`);
13
14 // Simule l'activation du confinement pour chaque secteur
15 this.affectedSectors = await Promise.all(
16 this.sectors.map(async (sector) => {
17 try {
18 // Dans un vrai système, ce serait un appel API
19 await this.activateSectorLockdown(sector);
20 return {
21 sector,
22 status: 'secured',
23 securedAt: new Date()
24 };
25 } catch (error) {
26 console.error(`Erreur de confinement du secteur ${sector} : `, error);
27 return {
28 sector,
29 status: 'failed',
30 error: error.message
31 };
32 }
33 })
34 );
35
36 this.active = true;
37
38 // Renvoie l'état
39 return {
40 active: this.active,
41 affectedSectors: this.affectedSectors,
42 initializedAt: new Date()
43 };
44 }
45
46 async shutdown() {
47 if (!this.active) {
48 return { success: false, reason: 'Protocole non actif' };
49 }
50
51 console.log(`Désactivation du protocole de confinement pour les secteurs : ${this.sectors.join(', ')}`);
52
53 // Simule la désactivation du confinement pour chaque secteur
54 await Promise.all(
55 this.sectors.map(async (sector) => {
56 try {
57 // Dans un vrai système, ce serait un appel API
58 await this.deactivateSectorLockdown(sector);
59 } catch (error) {
60 console.error(`Erreur de déverrouillage du secteur ${sector} : `, error);
61 }
62 })
63 );
64
65 this.active = false;
66
67 return {
68 success: true,
69 deactivatedAt: new Date()
70 };
71 }
72
73 // Méthode simulant l'activation du confinement d'un secteur
74 async activateSectorLockdown(sector) {
75 return new Promise((resolve, reject) => {
76 // Simule un délai réseau et une réponse API
77 setTimeout(() => {
78 if (Math.random() > 0.1) {
79 resolve({
80 sector,
81 status: 'secured',
82 gates: { closed: 4, total: 4 },
83 fences: { energized: true }
84 });
85 } else {
86 reject(new Error(`Erreur de confinement du secteur ${sector} : panne système`));
87 }
88 }, 500 + Math.random() * 1000);
89 });
90 }
91
92 // Méthode simulant la désactivation du confinement d'un secteur
93 async deactivateSectorLockdown(sector) {
94 return new Promise((resolve, reject) => {
95 // Simule un délai réseau et une réponse API
96 setTimeout(() => {
97 if (Math.random() > 0.05) {
98 resolve({
99 sector,
100 status: 'unlocked',
101 gates: { closed: 0, total: 4 },
102 fences: { energized: true } // Les clôtures restent sous tension
103 });
104 } else {
105 reject(new Error(`Erreur de déverrouillage du secteur ${sector} : panne système`));
106 }
107 }, 300 + Math.random() * 700);
108 });
109 }
110}Exemple de procédure d'urgence (dinosaurEscape.js)
1// emergencies/dinosaurEscape.js
2export class EmergencyProcedure {
3 constructor(location, options = {}) {
4 this.location = location;
5 this.options = options;
6 this.id = 'EM-' + Date.now().toString(36).toUpperCase();
7 this.status = 'initialized';
8 this.startTime = new Date();
9 }
10
11 async execute() {
12 console.log(`Exécution de la procédure d'urgence pour une évasion de dinosaure à l'emplacement ${this.location}`);
13 this.status = 'in_progress';
14
15 try {
16 // 1. Active l'alarme de zone
17 await this.activateZoneAlarm(this.location);
18
19 // 2. Évacue les visiteurs de la zone menacée
20 await this.evacuateVisitors(this.location);
21
22 // 3. Dépêche l'équipe de sécurité
23 const securityTeam = await this.dispatchSecurityTeam(this.location);
24
25 // 4. Active le protocole de confinement dans ce secteur et les secteurs adjacents
26 const adjacentSectors = await this.getAdjacentSectors(this.location);
27 const allSectors = [this.location, ...adjacentSectors];
28 await this.activateLockdown(allSectors);
29
30 // 5. Notifie le personnel
31 await this.notifyStaff({
32 type: 'dinosaur_escape',
33 location: this.location,
34 securityTeam,
35 sectors: allSectors
36 });
37
38 this.status = 'completed';
39 console.log(`Procédure d'urgence ${this.id} terminée avec succès`);
40
41 return this.id;
42 } catch (error) {
43 this.status = 'failed';
44 console.error(`La procédure d'urgence ${this.id} a échoué : `, error);
45 throw error;
46 }
47 }
48
49 // Méthodes utilitaires...
50 async activateZoneAlarm(zone) {
51 console.log(`Activation de l'alarme dans la zone ${zone}`);
52 return { success: true, zone };
53 }
54
55 async evacuateVisitors(zone) {
56 console.log(`Évacuation des visiteurs de la zone ${zone}`);
57 return { success: true, evacuatedCount: Math.floor(Math.random() * 50) + 10 };
58 }
59
60 async dispatchSecurityTeam(zone) {
61 console.log(`Envoi de l'équipe de sécurité vers la zone ${zone}`);
62 return {
63 teamId: 'ST-' + Math.floor(Math.random() * 10) + 1,
64 members: Math.floor(Math.random() * 5) + 3,
65 estimatedArrival: new Date(Date.now() + 5 * 60 * 1000)
66 };
67 }
68
69 async getAdjacentSectors(sector) {
70 // Dans un vrai système, on récupérerait ceci depuis une base de données
71 const sectorMap = {
72 'A1': ['A2', 'B1'],
73 'A2': ['A1', 'A3', 'B2'],
74 'B1': ['A1', 'B2', 'C1'],
75 // ... d'autres correspondances
76 };
77
78 return sectorMap[sector] || [];
79 }
80
81 async activateLockdown(sectors) {
82 console.log(`Activation du confinement dans les secteurs : ${sectors.join(', ')}`);
83
84 // Importe dynamiquement le module de protocole de confinement
85 const LockdownModule = await import('../protocols/lockdown.js');
86 const lockdown = new LockdownModule.default({
87 sectors,
88 reason: 'Évasion de dinosaure'
89 });
90
91 return lockdown.initialize();
92 }
93
94 async notifyStaff(data) {
95 console.log(`Notification du personnel concernant l'urgence : `, data);
96 return { success: true, notifiedCount: Math.floor(Math.random() * 30) + 5 };
97 }
98}Construire un système dynamique avec des imports déterminés à l'exécution
On peut aussi créer des systèmes plus avancés où les chemins d'import sont déterminés à l'exécution :
1// dynamicModuleLoader.js
2export class ModuleLoader {
3 constructor(basePath = '') {
4 this.basePath = basePath;
5 this.cache = new Map();
6 }
7
8 async load(modulePath, ignoreCache = false) {
9 const fullPath = this.basePath ? `${this.basePath}/${modulePath}` : modulePath;
10
11 // Vérifie le cache sauf s'il est ignoré
12 if (!ignoreCache && this.cache.has(fullPath)) {
13 console.log(`Renvoi du module ${fullPath} depuis le cache`);
14 return this.cache.get(fullPath);
15 }
16
17 try {
18 console.log(`Chargement dynamique du module : ${fullPath}`);
19 const module = await import(fullPath);
20
21 // Sauvegarde dans le cache
22 this.cache.set(fullPath, module);
23
24 return module;
25 } catch (error) {
26 console.error(`Erreur de chargement du module ${fullPath} : `, error);
27 throw error;
28 }
29 }
30
31 async loadMultiple(modulePaths) {
32 try {
33 const modulePromises = modulePaths.map(path => this.load(path));
34 return await Promise.all(modulePromises);
35 } catch (error) {
36 console.error(`Erreur de chargement de plusieurs modules : `, error);
37 throw error;
38 }
39 }
40
41 clearCache(modulePath) {
42 if (modulePath) {
43 const fullPath = this.basePath ? `${this.basePath}/${modulePath}` : modulePath;
44 return this.cache.delete(fullPath);
45 } else {
46 // Vide tout le cache
47 this.cache.clear();
48 return true;
49 }
50 }
51}
52
53// DinosaurTrackingSystem.js
54import { ModuleLoader } from './dynamicModuleLoader.js';
55
56export class DinosaurTrackingSystem {
57 constructor() {
58 this.moduleLoader = new ModuleLoader('./dinosaurs');
59 this.trackers = new Map();
60 this.activeDinosaurs = new Set();
61 }
62
63 async initialize() {
64 // Charge les modules de base
65 const [baseUtils, trackingCore] = await this.moduleLoader.loadMultiple([
66 './utils.js',
67 './trackingCore.js'
68 ]);
69
70 this.utils = baseUtils;
71 this.trackingCore = trackingCore;
72
73 console.log(`Système de suivi des dinosaures initialisé.`);
74 }
75
76 async trackDinosaur(dinosaurId, species) {
77 if (this.trackers.has(dinosaurId)) {
78 console.log(`Le dinosaure ${dinosaurId} est déjà suivi.`);
79 return this.trackers.get(dinosaurId);
80 }
81
82 try {
83 // Charge dynamiquement le module de suivi pour l'espèce donnée
84 const specificTrackingModule = await this.moduleLoader.load(`./species/${species.toLowerCase()}.js`);
85
86 // Crée et initialise un tracker pour ce dinosaure
87 const tracker = new specificTrackingModule.default(dinosaurId, {
88 utils: this.utils,
89 core: this.trackingCore
90 });
91
92 await tracker.initialize();
93
94 // Sauvegarde le tracker et marque le dinosaure comme actif
95 this.trackers.set(dinosaurId, tracker);
96 this.activeDinosaurs.add(dinosaurId);
97
98 console.log(`Suivi du dinosaure ${dinosaurId} (${species}) démarré.`);
99 return tracker;
100 } catch (error) {
101 console.error(`Erreur de suivi du dinosaure ${dinosaurId} : `, error);
102
103 // S'il n'y a pas de module spécifique pour l'espèce, utilise le module générique
104 try {
105 console.log(`Tentative d'utilisation du module de suivi générique pour ${species}`);
106 const genericTrackingModule = await this.moduleLoader.load('./genericTracker.js');
107
108 const tracker = new genericTrackingModule.default(dinosaurId, species, {
109 utils: this.utils,
110 core: this.trackingCore
111 });
112
113 await tracker.initialize();
114
115 this.trackers.set(dinosaurId, tracker);
116 this.activeDinosaurs.add(dinosaurId);
117
118 console.log(`Suivi générique du dinosaure ${dinosaurId} (${species}) démarré.`);
119 return tracker;
120 } catch (fallbackError) {
121 console.error(`Échec de l'utilisation même du module de suivi générique : `, fallbackError);
122 throw new Error(`Impossible de suivre le dinosaure ${dinosaurId} : ${error.message}`);
123 }
124 }
125 }
126
127 async stopTracking(dinosaurId) {
128 if (!this.trackers.has(dinosaurId)) {
129 console.log(`Le dinosaure ${dinosaurId} n'est pas suivi.`);
130 return false;
131 }
132
133 try {
134 const tracker = this.trackers.get(dinosaurId);
135 await tracker.shutdown();
136
137 this.trackers.delete(dinosaurId);
138 this.activeDinosaurs.delete(dinosaurId);
139
140 console.log(`Suivi du dinosaure ${dinosaurId} arrêté.`);
141 return true;
142 } catch (error) {
143 console.error(`Erreur lors de l'arrêt du suivi du dinosaure ${dinosaurId} : `, error);
144 return false;
145 }
146 }
147
148 getActiveTrackingCount() {
149 return this.activeDinosaurs.size;
150 }
151
152 getTrackerInfo(dinosaurId) {
153 const tracker = this.trackers.get(dinosaurId);
154 if (!tracker) {
155 return null;
156 }
157
158 return {
159 dinosaurId,
160 species: tracker.species,
161 lastKnownLocation: tracker.getLastLocation(),
162 status: tracker.getStatus(),
163 isActive: tracker.isActive()
164 };
165 }
166}
167
168// Utilisation du système de suivi
169async function initTrackingSystem() {
170 const trackingSystem = new DinosaurTrackingSystem();
171 await trackingSystem.initialize();
172
173 // Suit différentes espèces de dinosaures
174 try {
175 const rexTracker = await trackingSystem.trackDinosaur('rex-01', 'tyrannosaurus');
176 console.log('État du T-Rex :', rexTracker.getStatus());
177
178 const raptorTracker = await trackingSystem.trackDinosaur('raptor-02', 'velociraptor');
179 console.log('État du Velociraptor :', raptorTracker.getStatus());
180
181 // Même sans module spécifique, le système devrait utiliser le repli
182 const stegoTracker = await trackingSystem.trackDinosaur('stego-01', 'stegosaurus');
183 console.log('État du Stegosaurus :', stegoTracker.getStatus());
184
185 console.log(`Suivis actifs : ${trackingSystem.getActiveTrackingCount()} dinosaures`);
186 } catch (error) {
187 console.error(`Erreur d'initialisation du système de suivi : `, error);
188 }
189}
190
191// Initialise le système
192initTrackingSystem();Avantages et inconvénients de l'import dynamique
Avantages
- Chargement initial réduit - l'application ne charge que les modules nécessaires à un instant donné
- Meilleures performances - le découpage du code (code splitting) permet un chargement plus rapide de la page
- Flexibilité - possibilité de charger des modules selon des conditions ou des interactions utilisateur
- Imports conditionnels - possibilité de choisir différentes implémentations selon l'environnement, les paramètres, etc.
- Maintenance facilitée - découpage de l'application en modules plus petits et indépendants
Inconvénients
- Complexité - ajoute une couche de complexité à l'application
- Gestion des erreurs - nécessite une gestion soignée des erreurs pour les cas où un module ne peut pas être chargé
- Délais - charger un module à l'exécution peut introduire des délais dans l'interface utilisateur
- Augmentation de la taille du bundle - dans certains cas, cela peut entraîner une duplication de code
- Débogage - peut être plus difficile à déboguer que les imports statiques
Implémentation dans différents environnements
Navigateurs
Dans les navigateurs modernes, l'import dynamique fonctionne nativement :
1// Exemple navigateur
2document.getElementById('btnActivateLockdown').addEventListener('click', async () => {
3 try {
4 const lockdownModule = await import('./security/lockdown.js');
5 const lockdown = new lockdownModule.default();
6 lockdown.activateForSectors(['A1', 'B2']);
7 } catch (error) {
8 console.error('Échec du chargement du module de confinement :', error);
9 }
10});Node.js
Node.js prend officiellement en charge les imports dynamiques depuis la version 13.2.0 :
1// Exemple Node.js
2async function runDinosaurAnalysis(species) {
3 try {
4 // Import dynamique du module d'analyse pour l'espèce donnée
5 const analyzer = await import(`./analyzers/${species.toLowerCase()}.js`);
6 return analyzer.analyzeGenome();
7 } catch (error) {
8 console.error(`Pas d'analyseur pour l'espèce ${species} : `, error);
9 // Utilise l'analyseur générique
10 const genericAnalyzer = await import('./analyzers/generic.js');
11 return genericAnalyzer.analyzeGenome(species);
12 }
13}
14
15// Utilisation
16runDinosaurAnalysis('velociraptor')
17 .then(results => console.log("Résultats de l'analyse du génome :", results))
18 .catch(error => console.error("Erreur d'analyse :", error));Webpack et autres bundlers
Les bundlers comme Webpack, Rollup ou Parcel détectent automatiquement les imports dynamiques et découpent le code en plus petits chunks :
1// Webpack comprendra et gérera automatiquement cet import
2const loadAdminPanel = async () => {
3 // Un chunk séparé sera généré pour adminPanel
4 const adminModule = await import(/* webpackChunkName: "admin" */ './adminPanel');
5 return new adminModule.default();
6};
7
8// Tu peux même indiquer la priorité de chargement
9const prefetchReportGenerator = () => {
10 // Récupère le module à l'avance, mais ne l'exécute que quand c'est nécessaire
11 import(/* webpackPrefetch: true */ './reportGenerator');
12};Résumé
L'import dynamique de modules est une fonctionnalité puissante qui apporte plus de flexibilité et de performances aux applications JavaScript. Dans le contexte de notre Jurassic Park, il nous donne la possibilité de construire des systèmes capables de réagir dynamiquement à diverses situations, ce qui est crucial pour la sécurité d'un parc rempli de dinosaures dangereux !
Grâce aux imports dynamiques, on peut :
- Charger les modules uniquement quand on en a besoin
- Choisir différentes implémentations selon le contexte
- Réduire le temps de chargement initial de l'application
- Créer des systèmes flexibles avec des modules déterminés à l'exécution
Dans l'exercice suivant, nous examinerons les outils de débogage JavaScript, qui seront précieux quand quelque chose tournera mal dans nos systèmes de gestion du parc !
Code de cette leçon : main.js
1// Dynamic module importing - Jurassic Park
2// Lazy loading i Code Splitting
3
4console.log("Dynamic Module Importing");
5console.log("On-demand loading system\n");
6
7// ===========================================
8// DYNAMIC MODULE SIMULATION
9// ===========================================
10
11// Simulated modules as functions
12const modules = {
13 '/modules/trex-handler.js': () => ({
14 name: 'T-Rex Handler Module',
15 handleTRex: (trex) => {
16 console.log(`T-Rex handling: ${trex.name}`);
17 console.log(` Danger level: ${trex.dangerLevel}/10`);
18 console.log(` Status: Double protection required`);
19 return {
20 securityLevel: 'MAXIMUM',
21 requiredGuards: 4,
22 specialEquipment: ['Tranquilizer', 'Electric Fence', 'Tracker']
23 };
24 }
25 }),
26
27 '/modules/herbivore-handler.js': () => ({
28 name: 'Herbivore Handler Module',
29 handleHerbivore: (dino) => {
30 console.log(`Herbivore handling: ${dino.name}`);
31 console.log(` Danger level: ${dino.dangerLevel}/10`);
32 console.log(` Status: Standardowa ochrona`);
33 return {
34 securityLevel: 'LOW',
35 requiredGuards: 1,
36 specialEquipment: ['Food Dispenser', 'Health Monitor']
37 };
38 }
39 }),
40
41 '/modules/raptor-handler.js': () => ({
42 name: 'Raptor Handler Module',
43 handleRaptor: (raptor) => {
44 console.log(`Velociraptor handling: ${raptor.name}`);
45 console.log(` Danger level: ${raptor.dangerLevel}/10`);
46 console.log(` Status: High alert - they hunt in packs!`);
47 return {
48 securityLevel: 'HIGH',
49 requiredGuards: 3,
50 specialEquipment: ['Pack Tracker', 'Sound System', 'Emergency Protocol']
51 };
52 }
53 }),
54
55 '/modules/analytics.js': () => ({
56 name: 'Analytics Module',
57 generateReport: (dinosaurs) => {
58 const total = dinosaurs.length;
59 const carnivores = dinosaurs.filter(d => d.diet === 'carnivore').length;
60 const herbivores = dinosaurs.filter(d => d.diet === 'herbivore').length;
61 const avgDanger = dinosaurs.reduce((sum, d) => sum + d.dangerLevel, 0) / total;
62
63 return {
64 totalDinosaurs: total,
65 carnivores,
66 herbivores,
67 averageDangerLevel: avgDanger.toFixed(2),
68 recommendation: avgDanger > 6 ? 'Increase security' : 'Security level OK'
69 };
70 }
71 }),
72
73 '/modules/emergency.js': () => ({
74 name: 'Emergency Protocol Module',
75 activateEmergency: (reason) => {
76 console.log(`\nEMERGENCY PROTOCOL ACTIVATED!`);
77 console.log(`Reason: ${reason}`);
78 console.log(`1. Closing all paddocks`);
79 console.log(`2. Guest evacuation`);
80 console.log(`3. Security team mobilization`);
81 return {
82 status: 'ACTIVE',
83 timestamp: new Date().toISOString(),
84 actions: ['LOCK_ENCLOSURES', 'EVACUATE', 'ALERT_SECURITY']
85 };
86 }
87 })
88};
89
90// Simulation import() pour la démonstration
91async function dynamicImport(path) {
92 console.log(`⏳ Loading module: ${path}...`);
93
94 // Simulation network delay
95 await new Promise(resolve => setTimeout(resolve, 500));
96
97 if (modules[path]) {
98 const module = modules[path]();
99 console.log(`Loaded: ${module.name}\n`);
100 return module;
101 }
102
103 throw new Error(`Module not found: ${path}`);
104}
105
106// ===========================================
107// DINOSAUR MANAGEMENT SYSTEM
108// ===========================================
109
110class DinosaurManager {
111 constructor() {
112 this.dinosaurs = [];
113 this.loadedModules = new Map();
114 }
115
116 addDinosaur(name, species, diet, dangerLevel) {
117 const dinosaur = { name, species, diet, dangerLevel };
118 this.dinosaurs.push(dinosaur);
119 console.log(`Added dinosaura: ${name} (${species})`);
120 return dinosaur;
121 }
122
123 async handleDinosaur(dinosaur) {
124 console.log(`\nTraitement : ${dinosaur.name}...`);
125
126 // Dynamic loading of appropriate module
127 let modulePath;
128 let handlerMethod;
129
130 if (dinosaur.species === 'Tyrannosaurus Rex') {
131 modulePath = '/modules/trex-handler.js';
132 handlerMethod = 'handleTRex';
133 } else if (dinosaur.species === 'Velociraptor') {
134 modulePath = '/modules/raptor-handler.js';
135 handlerMethod = 'handleRaptor';
136 } else if (dinosaur.diet === 'herbivore') {
137 modulePath = '/modules/herbivore-handler.js';
138 handlerMethod = 'handleHerbivore';
139 } else {
140 console.log(`No dedicated module for ${dinosaur.species}`);
141 return null;
142 }
143
144 // Check cache
145 if (!this.loadedModules.has(modulePath)) {
146 const module = await dynamicImport(modulePath);
147 this.loadedModules.set(modulePath, module);
148 } else {
149 console.log(`Used cached module: ${modulePath}\n`);
150 }
151
152 const handler = this.loadedModules.get(modulePath);
153 return handler[handlerMethod](dinosaur);
154 }
155
156 async generateAnalytics() {
157 console.log(`\nGenerowanie raportu analitycznego...`);
158
159 if (!this.loadedModules.has('/modules/analytics.js')) {
160 const module = await dynamicImport('/modules/analytics.js');
161 this.loadedModules.set('/modules/analytics.js', module);
162 }
163
164 const analytics = this.loadedModules.get('/modules/analytics.js');
165 return analytics.generateReport(this.dinosaurs);
166 }
167
168 async triggerEmergency(reason) {
169 const module = await dynamicImport('/modules/emergency.js');
170 return module.activateEmergency(reason);
171 }
172}
173
174// ===========================================
175// DEMONSTRACJA
176// ===========================================
177
178async function runDemo() {
179 console.log("=== START DEMONSTRACJI ===\n");
180
181 const manager = new DinosaurManager();
182
183 // Ajout des dinosaures
184 console.log("Rejestracja dinosaurs:");
185 const trex = manager.addDinosaur('Rexy', 'Tyrannosaurus Rex', 'carnivore', 9);
186 const raptor1 = manager.addDinosaur('Blue', 'Velociraptor', 'carnivore', 7);
187 const raptor2 = manager.addDinosaur('Charlie', 'Velociraptor', 'carnivore', 7);
188 const brachi = manager.addDinosaur('Gentle Giant', 'Brachiosaurus', 'herbivore', 2);
189 const trice = manager.addDinosaur('Spike', 'Triceratops', 'herbivore', 3);
190
191 console.log(`\\nNombre total de dinosaures : ${manager.dinosaurs.length}`);
192
193 // Dynamic module loading for each dinosaur
194 console.log("\n=== ON-DEMAND MODULE LOADING ===");
195
196 const trexProtocol = await manager.handleDinosaur(trex);
197 console.log('Security protocol:', trexProtocol);
198
199 const raptor1Protocol = await manager.handleDinosaur(raptor1);
200 console.log('Security protocol:', raptor1Protocol);
201
202 const raptor2Protocol = await manager.handleDinosaur(raptor2);
203 console.log('Security protocol:', raptor2Protocol);
204
205 const brachiProtocol = await manager.handleDinosaur(brachi);
206 console.log('Security protocol:', brachiProtocol);
207
208 const triceProtocol = await manager.handleDinosaur(trice);
209 console.log('Security protocol:', triceProtocol);
210
211 // Report analityczny
212 console.log("\n=== ANALITYKA ===");
213 const report = await manager.generateAnalytics();
214 console.log('\nReport:', JSON.stringify(report, null, 2));
215
216 // Situation d'urgence
217 if (parseFloat(report.averageDangerLevel) > 6) {
218 const emergency = await manager.triggerEmergency('High average danger level');
219 console.log('\nStatus awaryjny:', emergency);
220 }
221
222 console.log(`\nLoaded modules: ${manager.loadedModules.size}`);
223 console.log(`Liste : ${Array.from(manager.loadedModules.keys()).join(', ')}`);
224
225 console.log("\n=== ZALETY DYNAMICZNEGO IMPORTOWANIA ===\n");
226 console.log("Lazy Loading:");
227 console.log(" - Modules loaded only when needed");
228 console.log(" - Szybszy start aplikacji\n");
229
230 console.log("Code Splitting:");
231 console.log(" - Smaller initial bundling");
232 console.log(" - Better memory usage\n");
233
234 console.log("Conditional Loading:");
235 console.log(" - Loading based on runtime conditions");
236 console.log(" - Flexibility in resource management\n");
237
238 console.log("Caching:");
239 console.log(" - Once loaded, module is cached");
240 console.log(" - Faster access on subsequent calls\n");
241
242 console.log("=== SYNTAX ===\n");
243 console.log("// L'import dynamique renvoie une Promise");
244 console.log("import('./module.js')");
245 console.log(" .then(module => {");
246 console.log(" module.doSomething();");
247 console.log(" })");
248 console.log(" .catch(err => console.error(err));\n");
249
250 console.log("// Lub z async/await");
251 console.log("async function loadModule() {");
252 console.log(" const module = await import('./module.js');");
253 console.log(" module.doSomething();");
254 console.log("}\n");
255
256 console.log("Demonstracja completed!");
257}
258
259// Lance la démo
260runDemo().catch(console.error);Tu as repéré une erreur dans cette leçon ?
Tâches pratiques dans le jeu
- Clique dans l'ordre
Ordonne correctement les éléments d'un export par défaut :
- Clique dans l'ordre
Ordonne les éléments d'export d'un module JavaScript :
- Tri horizontal
Ordonne les éléments d'import d'un module JavaScript :