0%
JavaScript Moderne : Les Modules ES

Modules ES

Organisation Modulaire du Code

10-15 min

JavaScript Moderne : Les Modules ES

Les modules ES (ECMAScript) sont un système standardisé pour organiser et partager du code JavaScript entre différents fichiers. Ils permettent de créer des applications plus maintenables et modulaires.

Introduction aux Modules ES

Pourquoi utiliser les modules ?

  • Encapsulation : Chaque module a son propre scope
  • Réutilisabilité : Le code peut être facilement partagé entre fichiers
  • Dépendances explicites : Les relations entre modules sont clairement définies
  • Organisation : Structure claire du code

Configuration de base

<!-- Dans votre HTML -->
<script type="module" src="main.js"></script>
// Dans votre package.json
{
    "type": "module"
}

Export de Modules

Export nommé

// mathematiques.js
export const PI = 3.14159;

export function addition(a, b) {
    return a + b;
}

export function soustraction(a, b) {
    return a - b;
}

// Alternative : export groupé
const multiplication = (a, b) => a * b;
const division = (a, b) => a / b;

export { multiplication, division };

Export par défaut

// calculateur.js
class Calculateur {
    addition(a, b) {
        return a + b;
    }
    
    soustraction(a, b) {
        return a - b;
    }
}

export default Calculateur;

Combinaison d’exports

// utils.js
export const VERSION = "1.0.0";

export function formaterNombre(nombre) {
    return nombre.toFixed(2);
}

const Utils = {
    VERSION,
    formaterNombre
};

export default Utils;

Import de Modules

Import nommé

// app.js
import { addition, soustraction } from './mathematiques.js';
import { PI as MATH_PI } from './mathematiques.js';

console.log(addition(2, 3));      // 5
console.log(MATH_PI);             // 3.14159

Import par défaut

// app.js
import Calculateur from './calculateur.js';

const calc = new Calculateur();
console.log(calc.addition(5, 3)); // 8

Import combiné

// app.js
import Utils, { VERSION } from './utils.js';
// ou
import Utils, * as UtilsAll from './utils.js';

console.log(Utils.formaterNombre(3.14159)); // "3.14"
console.log(VERSION);                       // "1.0.0"

Import dynamique

// app.js
async function chargerModule() {
    try {
        const module = await import('./grand-module.js');
        console.log(module.fonctionImportante());
    } catch (erreur) {
        console.error('Erreur de chargement:', erreur);
    }
}

Organisation des Modules

Structure de projet typique

src/
├── index.js
├── modules/
│   ├── utilisateurs/
│   │   ├── index.js
│   │   ├── utilisateur.js
│   │   └── validation.js
│   ├── produits/
│   │   ├── index.js
│   │   └── produit.js
│   └── utils/
│       ├── index.js
│       ├── format.js
│       └── validation.js

Modules de barils (Barrel modules)

// modules/utilisateurs/index.js
export { default as Utilisateur } from './utilisateur.js';
export { validerEmail, validerMotDePasse } from './validation.js';

// app.js
import { Utilisateur, validerEmail } from './modules/utilisateurs';

Modules agrégateurs

// modules/index.js
export * from './utilisateurs';
export * from './produits';
export * from './utils';

// app.js
import { Utilisateur, Produit, formaterPrix } from './modules';

Patterns et bonnes pratiques

Pattern de singleton

// config.js
class Config {
    constructor() {
        this.api = 'https://api.example.com';
        this.timeout = 5000;
    }
    
    setApi(url) {
        this.api = url;
    }
}

export default new Config();

Pattern de fabrique (Factory)

// loggerFactory.js
const createLogger = (prefix = '') => ({
    log: (message) => console.log(`${prefix}${message}`),
    error: (message) => console.error(`${prefix}${message}`),
    warn: (message) => console.warn(`${prefix}${message}`)
});

export default createLogger;

Pattern d’adaptateur

// apiAdapter.js
import axios from 'axios';
import { formatResponse } from './utils.js';

export const api = {
    async get(url) {
        const response = await axios.get(url);
        return formatResponse(response.data);
    },
    
    async post(url, data) {
        const response = await axios.post(url, data);
        return formatResponse(response.data);
    }
};

Cas d’utilisation pratiques

Application modulaire

// modules/auth/auth.service.js
export class AuthService {
    async login(credentials) {
        // Logique d'authentification
    }
    
    async logout() {
        // Logique de déconnexion
    }
}

// modules/auth/index.js
export { AuthService } from './auth.service.js';

// app.js
import { AuthService } from './modules/auth';
const auth = new AuthService();

Gestion d’état modulaire

// store/counter.js
let count = 0;
const listeners = new Set();

export const getCount = () => count;

export const increment = () => {
    count++;
    notifyListeners();
};

export const decrement = () => {
    count--;
    notifyListeners();
};

export const subscribe = (listener) => {
    listeners.add(listener);
    return () => listeners.delete(listener);
};

function notifyListeners() {
    listeners.forEach(listener => listener(count));
}

// app.js
import { getCount, increment, subscribe } from './store/counter.js';

subscribe((newCount) => {
    console.log('Nouveau compte:', newCount);
});

Plugins modulaires

// plugins/logger.js
export const loggerPlugin = {
    install(app) {
        app.logger = {
            log: console.log,
            error: console.error
        };
    }
};

// plugins/storage.js
export const storagePlugin = {
    install(app) {
        app.storage = {
            get: key => localStorage.getItem(key),
            set: (key, value) => localStorage.setItem(key, value)
        };
    }
};

// app.js
import { loggerPlugin } from './plugins/logger.js';
import { storagePlugin } from './plugins/storage.js';

class App {
    constructor() {
        this.plugins = new Map();
    }
    
    use(plugin) {
        plugin.install(this);
        this.plugins.set(plugin.name, plugin);
    }
}

const app = new App();
app.use(loggerPlugin);
app.use(storagePlugin);

Exercices pratiques

  1. Création d’une bibliothèque utilitaire
// utils/string.js
export const capitalize = (str) => 
    str.charAt(0).toUpperCase() + str.slice(1);

export const reverse = (str) => 
    str.split('').reverse().join('');

// utils/array.js
export const unique = (arr) => 
    [...new Set(arr)];

export const groupBy = (arr, key) => 
    arr.reduce((acc, item) => {
        (acc[item[key]] = acc[item[key]] || []).push(item);
        return acc;
    }, {});

// utils/index.js
export * from './string.js';
export * from './array.js';

// app.js
import { capitalize, unique } from './utils';

console.log(capitalize('hello')); // "Hello"
console.log(unique([1, 2, 2, 3])); // [1, 2, 3]
  1. Système de plugins modulaire
// plugin-system.js
export class PluginSystem {
    constructor() {
        this.plugins = new Map();
    }
    
    register(name, plugin) {
        if (this.plugins.has(name)) {
            throw new Error(`Plugin ${name} déjà enregistré`);
        }
        this.plugins.set(name, plugin);
        
        if (plugin.install) {
            plugin.install(this);
        }
    }
    
    use(name) {
        const plugin = this.plugins.get(name);
        if (!plugin) {
            throw new Error(`Plugin ${name} non trouvé`);
        }
        return plugin;
    }
}

// plugins/validation.js
export const validationPlugin = {
    name: 'validation',
    install(system) {
        system.validate = {
            email: (email) => /^.+@.+\..+$/.test(email),
            required: (value) => value !== undefined && value !== ''
        };
    }
};

// app.js
import { PluginSystem } from './plugin-system.js';
import { validationPlugin } from './plugins/validation.js';

const system = new PluginSystem();
system.register(validationPlugin.name, validationPlugin);

console.log(system.validate.email('test@example.com')); // true

Conclusion

Les modules ES sont un outil fondamental pour :

  • Organiser le code de manière modulaire et maintenable
  • Gérer efficacement les dépendances entre différentes parties de l’application
  • Créer des systèmes extensibles via des plugins
  • Améliorer la réutilisabilité du code

Dans le prochain tutoriel, nous explorerons les structures de données modernes comme Map et Set.


Besoin de réviser les Promises et async/await ? Consultez notre tutoriel précédent sur la programmation asynchrone.

Commentaires

Les commentaires sont alimentés par GitHub Discussions

Connectez-vous avec GitHub pour participer à la discussion

Lien copié !