Comprendre (tous) les formats et outils JavaScript «modulaires»



Bonjour mes amis!

Je vous présente la traduction de l'article «Comprendre (tous) les formats et outils du module JavaScript» par Dixin.

Lors de la création d'une application, on souhaite souvent diviser le code en parties, blocs logiques ou fonctionnels (modules). Cependant, JavaScript n'avait initialement aucun support de module. Cela a conduit à l'émergence de diverses technologies modulaires. Cet article présente tous les concepts de base, modèles, bibliothèques, syntaxe et outils pour travailler avec des modules en JavaScript.


Module IIFE: modèle de module JS


En définissant une variable dans JS, nous la définissons comme une variable globale. Cela signifie qu'une telle variable sera disponible dans tous les fichiers JS chargés sur la page actuelle:

    //   
    let count = 0
    const increase = () => ++count
    const reset = () => {
        count = 0
        console.log(' .')
    }

    //   
    increase()
    reset()

Afin d'éviter la pollution de l'espace de noms global, vous pouvez utiliser une fonction anonyme:

    (() => {
        let count = 0
        // ...
    })

Voila, il n'y a plus de variables globales. Cependant, le code à l'intérieur de la fonction n'est pas exécuté.

IIFE: expression de fonction immédiate


Pour exécuter du code à l'intérieur d'une fonction f, elle doit être appelée en utilisant ()how f(). Pour exécuter du code à l'intérieur d'une fonction anonyme, il (() => {})faut également utiliser (). Cela ressemble à ceci (() => {})():

    (() => {
        let count = 0
        // ...
    })()

C'est ce qu'on appelle IIFE (immédiatement appelé expression de fonction). Un module peut être défini comme suit:

    //  IIFE 
    const iifeCounterModule = (() => {
        let count = 0
        return {
            increase: () => ++count,
            reset: () => {
                count = 0
                console.log(' .')
            }
        }
    })()

    //  IIFE 
    iifeCounterModule.increase()
    iifeCounterModule.reset()

Nous enveloppons le code du module dans IIFE. Une fonction anonyme renvoie un objet. Cela remplace l'interface d'exportation. Il n'y a qu'une seule variable globale - le nom du module (ou son espace de noms). Par la suite, le nom du module peut être utilisé pour l'appeler (export). C'est ce qu'on appelle le modèle de module JS.

Impuretés d'importation


Lors de la définition d'un module, certaines dépendances peuvent être requises. Lorsque vous utilisez un modèle modulaire, chaque module dépendant est une variable globale. Les modules dépendants peuvent être définis à l'intérieur d'une fonction anonyme ou passés à elle comme arguments:

    //  IIFE   
    const iifeCounterModule = ((dependencyModule1, dependencyModule2) => {
        let count = 0
        return {
            increase: () => ++count,
            reset: () => {
                count = 0
                console.log(' .')
            }
        }
    })(dependencyModule1, dependencyModule2)

Les versions antérieures de bibliothèques populaires telles que jQuery utilisaient ce modèle (la dernière version de jQuery utilise le module UMD).

Module ouvert: modèle de module JS ouvert


Le modèle de module ouvert a été inventé par Christian Heilmann. Ce modèle est également IIFE, mais l'accent est mis sur la définition de toutes les interfaces en tant que variables locales à l'intérieur d'une fonction anonyme:

    //   
    const revealingCounterModule = (() => {
        let count = 0
        const increase = () => ++count
        const reset = () => {
            count = 0
            console.log(' .')
        }

        return {
            increase,
            reset
        }
    })()

    //   
    revealingCounterModule.increase()
    revealingCounterModule.reset()

Cette syntaxe permet de mieux comprendre ce dont chaque interface est responsable (ou ce qu'elle fait).

Module CJS: module CommonJS ou module Node.js


CommonJS, initialement nommé ServerJS, est un modèle pour définir et utiliser des modules. Il est intégré à Node.js. Par défaut, chaque fichier JS est un CJS. Les variables permettent moduleégalement exportsd'exporter le module (fichier). La fonction requirepermet de charger et d'utiliser le module. Le code suivant illustre la définition d'un module de compteur dans la syntaxe CommonJS:

    //  CommonJS : commonJSCounterModule.js
    const dependencyModule1 = require('./dependencyModule1')
    const dependencyModule2 = require('./dependencyModule2')

    let count = 0
    const increase = () => ++count
    const reset = () => {
        count = 0
        console.log(' .')
    }

    exports.increase = increase
    exports.reset = reset
    //  ()
    module.exports = {
        increase,
        reset
    }

Voici comment ce module est utilisé:

    //  CommonJS 
    const {
        increase,
        reset
    } = require('./commonJSCounterModule')
    increase()
    reset()
    // 
    const commonJSCounterModule = require('./commonJSCounterModule')
    commonJSCounterModule.increase()
    commonJSCounterModule.reset()

Dans le runtime Node.js (moteur), ce modèle est utilisé en encapsulant le code à l'intérieur du fichier dans une fonction, à laquelle les variables exports, moduleet une fonction sont passées en tant que paramètres require:

    //  CommonJS 
    (function(exports, require, module, __filename, __dirname) {
        const dependencyModule1 = require('./dependencyModule1')
        const dependencyModule2 = require('./dependencyModule2')

        let count = 0
        const increase = () => ++count
        const reset = () => {
            count = 0
            console.log(' .')
        }

        module.exports = {
            increase,
            reset
        }

        return module.exports
    }).call(thisValue, exports, require, module, filename, dirname)

    //  CommonJS 
    (function(exports, require, module, __filename, __dirname) {
        const commonJSCounterModule = require('./commonJSCounterModule')
        commonJSCounterModule.increase()
        commonJSCounterModule.reset()
    }).call(thisValue, exports, require, module, filename, dirname)

Module AMD ou module RequireJS


AMD ( définition de module asynchrone ) est un modèle pour définir et utiliser des modules. Il est utilisé dans la bibliothèque RequireJS . AMD contient une fonction de definedéfinition de module qui accepte le nom du module, les noms de dépendance et la fonction d'usine:

    //  AMD 
    define('amdCounterModule', ['dependencyModule1', 'dependencyModule2'], (dependencyModule1, dependencyModule2) => {
        let count = 0
        const increase = () => ++count
        const reset = () => {
            count = 0
            console.log(' .')
        }

        return {
            increase,
            reset
        }
    })

Il contient également une fonction required'utilisation du module:

    //  AMD 
    require(['amdCounterModule'], amdCounterModule => {
        amdCounterModule.increase()
        amdCounterModule.reset()
    })


requireAMD diffère de requireCommonJS en ce qu'il prend les noms des modules et les modules eux-mêmes comme arguments de la fonction.

Chargement dynamique


La fonction a defineégalement un objectif différent. Il prend une fonction de rappel et transmet une fonction de type CommonJS requireà cette fonction. Dans la fonction de rappel, require est appelé pour charger dynamiquement le module:

    //   AMD 
    define(require => {
        const dynamicDependencyModule1 = require('dependencyModule1')
        const dynamicDependencyModule2 = require('dependencyModule2')

        let count = 0
        const increase = () => ++count
        const reset = () => {
            count = 0
            console.log(' .')
        }

        return {
            increase,
            reset
        }
    })

Module AMD du module CommonJS


La fonction ci - dessus define, en plus require, peut prendre les variables exportset comme arguments module. Par conséquent, le definecode de CommonJS peut être exécuté à l' intérieur :

    //  AMD   CommonJS 
    define((require, exports, module) => {
        // CommonJS 
        const dependencyModule1 = require('dependencyModule1')
        const dependencyModule2 = require('dependencyModule2')

        let count = 0
        const increase = () => ++count
        const reset = () => {
            count = 0
            console.log(' .')
        }

        exports.increase = increase
        exports.reset = reset
    })

    //  AMD   CommonJS 
    define(require => {
        // CommonJS 
        const counterModule = require('amdCounterModule')
        counterModule.increase()
        counterModule.reset()
    })

Module UMD: définition de module universel ou module UmdJS


UMD ( définition de module universel ) - un ensemble de modèles pour assurer le fonctionnement du module dans différents environnements d'exécution.

UMD pour AMD (RequireJS) et navigateur


Le code suivant fournit le module dans AMD (RequireJS) et le navigateur:

    //  UMD   AMD (RequireJS)  
    ((root, factory) => {
        //   define AMD/RequireJS
        if (typeof define === 'function' && define.amd) {
            //  ,      
            define('umdCounterModule', ['dependencyModule1', 'dependencyModule2'], factory)
        } else {
            //  ,    
            //      (  Window)
            //    
            root.umdCounterModule = factory(root.dependencyModule1, root.dependencyModule2)
        }
    })(typeof self !== undefined ? self : this, (dependencyModule1, dependencyModule2) => {
        //  
        let count = 0
        const increase = () => ++count
        const reset = () => {
            count = 0
            console.log(' ')
        }

        return {
            increase,
            reset
        }
    })

Ça a l'air compliqué, mais c'est juste de l'IIFE. Une fonction anonyme détermine s'il existe une fonction defined'AMD / RequireJS.

  • Si elle est definedétectée, la fonction d'usine est appelée à travers elle.
  • S'il definen'est pas trouvé, la fonction d'usine est appelée directement. À ce stade, l'argument rootest l'objet Window du navigateur. Il reçoit des modules dépendants de variables globales (propriétés de l'objet Window). Lorsqu'un factorymodule revient, il devient également une variable globale (une propriété de l'objet Window).

UMD pour AMD (RequireJS) et CommonJS (Node.js)


Le code suivant fournit le module à la fois dans AMD (RequireJS) et CommonJS (Node.js):

    (define => define((require, exports, module) => {
        //  
        const dependencyModule1 = require("dependencyModule1")
        const dependencyModule2 = require("dependencyModule2")

        let count = 0
        const increase = () => ++count
        const reset = () => {
            count = 0
            console.log("Count is reset.")
        }

        module.export = {
            increase,
            reset
        }
    }))( //   module  exports  CommonJS/Node.js
        //    define  AMD/RequireJS
        typeof module === 'object' && module.exports && typeof define !== 'function'
            ? // CommonJS/Node.js.    define
                factory => module.exports = factory(require, exports, module)
            : // AMD/RequireJS.   define
                define)

N'ayez pas peur, c'est juste de l'IIFE à nouveau. Lorsqu'une fonction anonyme est appelée, son argument est «évalué». Évaluer l'argument pour déterminer l'environnement d'exécution (défini par la présence de variables moduleet exportsde CommonJS / Node.js, et les fonctions definede AMD / RequireJS).

  • Si le runtime est CommonJS / Node.js, l'argument de fonction anonyme crée manuellement la fonction define.
  • Si le runtime est AMD / RequireJS, l'argument de la fonction anonyme est une fonction definede cet environnement. L'exécution d'une fonction anonyme garantit que la fonction fonctionne define. A l'intérieur d'une fonction anonyme, une fonction est appelée pour créer le module define.

Module ES: module ECMAScript2015 ou ES6


En 2015, la version 6 de la spécification JS a introduit une nouvelle syntaxe modulaire. Cela s'appelle ECMAScript 2015 (ES2015) ou ECMAScript 6 (ES6). La base de la nouvelle syntaxe est les mots import- clés et export. Le code suivant illustre l'utilisation du module ES pour l'importation / exportation nommée et par défaut (par défaut):

    //  ES : esCounterModule.js  esCounterModule.mjs
    import dependencyModule1 from './dependencyModule1.mjs'
    import dependencyModule2 from './dependencyModule2.mjs'

    let count = 0
    //  
    export const increase = () => ++count
    export const reset = () => {
        count = 0
        console.log(' .')
    }
    //    
    export default {
        increase,
        reset
    }

Pour utiliser le fichier de module dans le navigateur, ajouter la balise <script>et l' identifier comme un module: <script type="module" src="esCounterModule.js"></script>. Pour utiliser ce module dans Node.js, changez son extension en .mjs:

    //  ES 
    //    
    import {
        increase,
        reset
    } from './esCounterModule.mjs'
    increase()
    reset()
    //      
    import esCounterModule from './esCounterModule.mjs'
    esCounterModule.increase()
    esCounterModule.reset()


Pour une compatibilité descendante dans le navigateur, vous pouvez ajouter une balise <script>avec l'attribut nomodule:

    <nomodule de script>
        alert ('Non pris en charge.')
    </script>

Module dynamique ES: module dynamique ECMAScript2020 ou ES11


La dernière version 11 de la spécification JS 2020 introduit une fonction intégrée importpour l'utilisation dynamique des modules ES. Cette fonction renvoie une promesse, vous pouvez donc utiliser le module avec then:

    //   ES    ,    
    import('./esCounterModule.js').then(({
        increase,
        reset
    }) => {
        increase()
        reset()
    })
    //      
    import('./esCounterModule.js').then(dynamicESCounterModule => {
        dynamicESCounterModule.increase()
        dynamicESCounterModule.reset()
    })

Étant donné que la fonction importrenvoie une promesse, elle peut utiliser le mot clé await:

    //   ES   async/await
    (async () => {
        //    
        const {
            increase,
            reset
        } = await import('./esCounterModule.js')
        increase()
        reset()

        //      
        const dynamicESCounterModule = await import('./esCounterModule.js')
        dynamicESCounterModule.increase()
        dynamicESCounterModule.reset()
    })

Module système: Module SystemJS


SystemJS est une bibliothèque pour prendre en charge les modules ES dans les anciens navigateurs. Par exemple, le module suivant est écrit à l'aide de la syntaxe ES6:

    //  ES 
    import dependencyModule1 from "./dependencyModule1.js"
    import dependencyModule2 from "./dependencyModule2.js"
    dependencyModule1.api1()
    dependencyModule2.api2()

    let count = 0
    //  
    export const increase = function() {
        return ++count
    }
    export const reset = function() {
        count = 0
        console.log("Count is reset.")
    }
    //    
    export default {
        increase,
        reset
    }

Ce code ne fonctionnera pas dans les navigateurs qui ne prennent pas en charge la syntaxe ES6. Une solution à ce problème consiste à traduire le code à l'aide de l' System.registerinterface de la bibliothèque SystemJS:

    //  SystemJS 
    System.register(['./dependencyModule1', './dependencyModule2'], function(exports_1, context_1) {
        'use strict'
        var dependencyModule1_js_1, dependencyModule2_js_1, count, increase, reset
        var __moduleName = context_1 && context_1.id
        return {
            setters: [
                function(dependencyModule1_js_1_1) {
                    dependencyModule1_js_1 = dependencyModule1_js_1_1
                },
                function(dependencyModule2_js_1_1) {
                    dependencyModule2_js_1 = dependencyModule2_js_1_1
                }
            ],
            execute: function() {
                dependencyModule1_js_1.default.api1()
                dependencyModule2_js_1.default.api2()
                count = 0
                //  
                exports_1('increase', increase = function() {
                    return ++count
                })
                exports_1('reset', reset = function() {
                    count = 0
                    console.log(' .')
                })
                //    
                exports_1('default', {
                    increase,
                    reset
                })
            }
        }
    })

La nouvelle syntaxe ES6 modulaire a disparu. Mais le code fonctionnera bien dans les anciens navigateurs. Cette transpilation peut être effectuée automatiquement à l'aide de Webpack, TypeScript, etc.

Chargement de module dynamique


SystemJS contient également une fonction importd'importation dynamique:

    //  SystemJS   
    System.import('./esCounterModule.js').then(dynamicESCounterModule => {
        dynamicESCounterModule.increase()
        dynamicESCounterModule.reset()
    })

Module Webpack: compilation et assemblage des modules CJS, AMD et ES


Webpack est un constructeur de modules. Son transpilateur combine les modules CommonJS, AMD et ES dans un modèle modulaire équilibré unique et collecte tout le code dans un seul fichier. Par exemple, dans les 3 fichiers suivants, 3 modules sont définis en utilisant une syntaxe différente:

    //  AMD : amdDependencyModule1.js
    define('amdDependencyModule1', () => {
        const api1 = () => {}
        return {
            api1
        }
    })

    //  CommonJS : commonJSDependencyModule2.js
    const dependencyModule2 = require('./commonJSDependencyModule2')
    const api2 = () => dependencyModule1.api1()
    exports.api2 = api2

    //  ES : esCounterModule.js
    import dependencyModule1 from './amdDependencyModule1'
    import dependencyModule2 from './commonJSDependencyModule2'

    let count = 0
    const increase = () => ++count
    const reset = () => {
        count = 0
        console.log(' .')
    }

    export default {
        increase,
        reset
    }

Le code suivant montre l'utilisation de ce module:

    //  ES : index.js
    import counterModule from './esCounterModule'
    counterModule.increase()
    counterModule.reset()

Webpack est capable de combiner ces fichiers, malgré le fait qu'il s'agit de différents systèmes modulaires, en un seul fichier main.js:

    racine
        dist
            main.js (assemblage de fichiers situés dans le dossier src)
        src
            amdDependencyModule1.js
            commonJSDependencyModule2.js
            esCounterModule.js
            index.js
        webpack.config.js

Étant donné que Webpack est basé sur Node.js, il utilise la syntaxe modulaire de CommonJS. Dans webpack.config.js:

    const path = require('path')

    module.exports = {
        entry: './src/index.js',
        mode: 'none', //          
        output: {
            filename: 'main.js',
            path: path.resolve(__dirname, 'dist'),
        },
    }

Pour compiler et construire, vous devez exécuter les commandes suivantes:

    npm install webpack webpack-cli --save-dev
    npx webpack --config webpack.config.js

Par conséquent, Webpack créera le fichier main.js. Le code suivant est main.jsformaté pour améliorer la lisibilité:

    (function(modules) { //  Webpack
        //    
        var installedModules = {}
        //  require
        function require(moduleId) {
            // ,     
            if (installedModules[moduleId]) {
                return installedModules[moduleId].exports
            }
            //        
            var module = installedModules[moduleId] = {
                i: moduleId,
                l: false,
                exports: {}
            }
            //   module
            modules[moduleId].call(module.exports, module, module.exports, require)
            //    
            module.l = true
            //   exports 
            return module.exports
        }

        //   modules (__webpack_modules__)
        require.m = modules
        //    
        require.c = installedModules
        //     
        require.d = function(exports, name, getter) {
            if (!require.o(exports, name)) {
                Object.defineProperty(exports, name, {
                    enumerable: true,
                    get: getter
                })
            }
        }
        //  __esModule  exports
        require.r = function(exports) {
            if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
                Object.defineProperty(exports, Symbol.toStringTag, {
                    value: 'Module'
                })
            }
            Object.defineProperty(exports, '__esModule', {
                value: true
            })
        }
        //       
        // mode & 1:  -  ,  
        // mode & 2:       ns (namespace)
        // mode & 4:  ,   ns  
        // mode & 8|1:   require
        require.t = function(value, mode) {
            if (mode & 1) value = require(value)
            if (mode & 8) return value
            if ((mode & 4) && typeof value === 'object' && value && value.__esModule) return value
            var ns = Object.create(null)
            require.r(ns)
            Object.defineProperty(ns, 'default', {
                enumerable: true,
                value: value
            })
            if (mode & 2 && typeof value !== 'string')
                for (var key in value) require.d(ns, key, function(key) {
                    return value[key]
                }.bind(null, key))
            return ns
        }
        //  getDefaultExport      
        require.n = function(module) {
            var getter = module && module.__esModule ?
                function getDefault() {
                    return module['default']
                } :
                function getModuleExports() {
                    return module
                }
            require.d(getter, 'a', getter)
            return getter
        }
        // Object.prototype.hasOwnProperty.call
        require.o = function(object, property) {
            return Object.prototype.hasOwnProperty.call(object, property)
        }
        // __webpack_public_path__
        require.p = ''
        //      exports
        return require(require.s = 0)
    })([
        function(module, exports, require) {
            'use strict'
            require.r(exports)
            //  ES : index.js
            var esCounterModule = require(1)
            esCounterModule['default'].increase()
            esCounterModule['default'].reset()
        },
        function(module, exports, require) {
            'use strict'
            require.r(exports)
            //  ES : esCounterModule.js
            var amdDependencyModule1 = require.n(require(2))
            var commonJSDependencyModule2 = require.n(require(3))
            amdDependencyModule1.a.api1()
            commonJSDependencyModule2.a.api2()

            let count = 0
            const increase = () => ++count
            const reset = () => {
                count = 0
                console.log(' .')
            }

            exports['default'] = {
                increase,
                reset
            }
        },
        function(module, exports, require) {
            var result!(result = (() => {
                    //  AMD : amdDependencyModule1.js
                    const api1 = () => {}
                    return {
                        api1
                    }
                }).call(exports, require, exports, module),
                result !== undefined && (module.exports = result))
        },
        function(module, exports, require) {
            //  CommonJS : commonJSDependencyModule2.js
            const dependencyModule1 = require(2)
            const api2 = () => dependencyModule1.api1()
            exports.api2 = api2
        }
    ])

Et encore une fois, c'est juste IIFE. Le code de 4 fichiers est converti en un tableau de 4 fonctions. Et ce tableau est passé à la fonction anonyme en tant que paramètre.

Module Babel: transpilation du module ES


Babel est un autre transporteur pour prendre en charge le code ES6 + dans les anciens navigateurs. Le module ES6 + ci-dessus peut être converti en module Babel comme suit:

    // Babel
    Object.defineProperty(exports, '__esModule', {
        value: true
    })
    exports['default'] = void 0

    function _interopRequireDefault(obj) {
        return obj && obj.__esModule ? obj : {
            'default': obj
        }
    }

    //  ES : esCounterModule.js
    var dependencyModule1 = _interopRequireDefault(require('./amdDependencyModule1'))
    var dependencyModule2 = _interopRequireDefault(require('./commonJSDependencyModule2'))
    dependencyModule1['default'].api1()
    dependencyModule2['default'].api2()

    var count = 0
    var increase = function() {
        return ++count
    }
    var reset = function() {
        count = 0
        console.log(' .')
    }

    exports['default'] = {
        increase: increase,
        reset: reset
    }

Et voici le code en index.js, démontrant l'utilisation de ce module:

    // Babel
    function _interopRequireDefault(obj) {
        return obj && obj.__esModule ? obj : {
            'default': obj
        }
    }

    //  ES : index.js
    var esCounterModule = _interopRequireDefault(require('./esCounterModule.js'))
    esCounterModule['default'].increase()
    esCounterModule['default'].reset()

Il s'agit de la transpilation par défaut. Babel sait également travailler avec d'autres outils.

Babel et SystemJS


SystemJS peut être utilisé comme plugin pour Babel:

    npm install --save-dev @ babel / plugin-transform-modules-systemjs


Ce plugin doit être ajouté à babel.config.json:

    {
        'plugins': ['@ babel / plugin-transform-modules-systemjs'],
        'préconfigurations': [
            [
            '@ babel / env',
                {
                    "cibles": {
                        'ie': '11'
                    }
                }
            ]
        ]
    }

Babel peut désormais travailler avec SystemJS pour transpiler les modules CommonJS / Node.js, AMD / RequireJS et ES:

    npx babel src --out-dir lib

Résultat:

    racine
        lib
            amdDependencyModule1.js (transpilé à l'aide de SystemJS)
            commonJSDependencyModule2.js (transpilé à l'aide de SystemJS)
            esCounterModule.js (transpilé à l'aide de SystemJS)
            index.js (transpilé à l'aide de SystemJS)
        src
            amdDependencyModule1.js
            commonJSDependencyModule2.js
            esCounterModule.js
            index.js
        babel.config.json

La syntaxe complète des modules AMD, CommonJS et ES est transposée en syntaxe SystemJS:

    //  AMD/RequireJS   SystemJS : lib/amdDependencyModule1.js
    System.register([], function(_export, _context) {
        'use strict'
        return {
            setters: [],
            execute: function() {
                //  AMD : src/amdDependencyModule1.js
                define('amdDependencyModule1', () => {
                    const api1 = () => {}

                    return {
                        api1
                    }
                })
            }
        }
    })

    //  CommonJS/Node.js   SystemJS : lib/commonJSDependencyModule2.js.
    System.register([], function(_export, _context) {
        'use strict'
        var dependencyModule1, api2
        return {
            setters: [],
            execute: function() {
                //  CommonJS : src/commonJSDependencyModule2.js
                dependencyModule1 = require('./amdDependencyModule1')

                api2 = () => dependencyModule1.api1()

                exports.api2 = api2
            }
        }
    })

    //  ES   SystemJS 
    System.register(['./amdDependencyModule1', './commonJSDependencyModule2'], function(_export, _context) {
        var dependencyModule1, dependencyModule2, count, increase, reset
        return {
            setters: [function(_amdDependencyModule) {
                dependencyModule1 = _amdDependencyModule.default
            }, function(_commonJSDependencyModule) {
                dependencyModule2 = _commonJSDependencyModule.default
            }],
            execute: function() {
                //  ES : src/esCounterModule.js
                dependencyModule1.api1()
                dependencyModule1.api2()
                count = 0

                increase = () => ++count

                reset = () => {
                    count = 0
                    console.log(' .')
                }

                _export('default', {
                    increase,
                    reset
                })
            }
        }
    })

    //  ES   SystemJS : lib/index.js
    System.register(['./esCounterModule'], function(_export, _context) {
        var esCounterModule
        return {
            setters: [function(_esCounterModule) {
                esCounterModule = _esCounterModule.default
            }],
            execute: function() {
                //  ES : src/index.js
                esCounterModule.increase()
                esCounterModule.reset()
            }
        }
    })

Module TypeScript: transpilation des modules CJS, AMD, ES et SystemJS


TypeScript prend en charge toutes les versions de la syntaxe JS, y compris ES6. Pendant la transpilation, la syntaxe du module ES6 peut être enregistrée ou convertie dans un autre format, notamment CommonJS / Node.js, AMD / RequireJS, UMD / UmdJS ou SystemJS selon les paramètres de la transpilation dans tsconfig.json:

    {
        'compilerOptions': {
            'module': 'ES2020' // Aucun, CommonJS, AMD, System,
            UMD, ES6, ES2015, ESNext
        }
    }

Par exemple:

    // TypeScript  ES 
    //  compilerOptions: { module: 'ES6' }.    
    import dependencyModule from './dependencyModule'
    dependencyModule.api()
    let count = 0
    export const increase = function() {
        return ++count
    }

    //  compilerOptions: { module: 'CommonJS' }.   CommonJS/Node.js 
    var __importDefault = (this && this.__importDefault) || function(mod) {
        return (mod && mod.__esModule) ? mod : {
            'default': mod
        }
    }
    exports.__esModule = true

    var dependencyModule_1 = __importDefault(require('./dependencyModule'))
    dependencyModule_1['default'].api()
    var count = 0
    exports.increase = function() {
        return ++count
    }

    //  compilerOptions: { module: 'AMD' }.   AMD/RequireJS 
    var __importDefault = (this && this.__importDefault) || function(mod) {
        return (mod && mod.__esModule) ? mod : {
            'default': mod
        }
    }
    define(['require', 'exports', './dependencyModule'], function(require, exports, dependencyModule_1) {
        'use strict'
        exports.__esModule = true

        dependencyModule_1 = __importDefault(dependencyModule_1)
        dependencyModule_1['default'].api()
        var count = 0
        exports.increase = function() {
            return ++count
        }
    })

    //  compilerOptions: { module: 'UMD' }.   UMD/UmdJS 
    var __importDefault = (this & this.__importDefault) || function(mod) {
            return (mod && mod.__esModule) ? mod : {
                'default': mod
            }
        }
        (function(factory) {
            if (typeof module === 'object' && typeof module.exports === 'object') {
                var v = factory(require, exports)
                if (v !== undefined) module.exports = v
            } else if (typeof define === 'function' && define.amd) {
                define(['require', 'exports', './dependencyModule'], factory)
            }
        })(function(require, exports) {
            'use strict'
            exports.__esModule = true

            var dependencyModule_1 = __importDefault(require('./dependencyModule'))
            dependencyModule_1['default'].api()
            var count = 0
            exports.increase = function() {
                return ++count
            }
        })

    //  compilerOptions: { module: 'System' }.   System/SystemJS 
    System.register(['./dependencyModule'], function(exports_1, context_1) {
        'use strict'
        var dependencyModule_1, count, increase
        car __moduleName = context_1 && context_1.id
        return {
            setters: [
                function(dependencyModule_1_1) {
                    dependencyModule_1 = dependencyModule_1_1
                }
            ],
            execute: function() {
                dependencyModule_1['default'].api()
                count = 0
                exports_1('increase', increase = function() {
                    return ++count
                })
            }
        }
    })

La syntaxe ES modulaire prise en charge par TypeScript est appelée modules externes.

Modules internes et espace de noms


TypeScript a également des mots clés moduleet namespace. Ils sont appelés modules internes:

    module Counter {
        let count = 0
        export const increase = () => ++count
        export const reset = () => {
            count = 0
            console.log(' .')
        }
    }

    namespace Counter {
        let count = 0
        export const increase = () => ++count
        export const reset = () => {
            count = 0
            console.log(' .')
        }
    }

Les deux sont transposés en objets JS:

    var Counter;
    (function(Counter) {
        var count = 0
        Counter.increase = function() {
            return ++count
        }
        Counter.reset = function() => {
            count = 0
            console.log(' .')
        }
    })(Counter || (Counter = {}))

Le module TypeScript et l'espace de noms peuvent avoir plusieurs niveaux d'imbrication via le séparateur .:

    module Counter.Sub {
        let count = 0
        export const increase = () => ++count
    }

    namespace Counter.Sub {
        let count = 0
        export const increase = () => ++count
    }

Le sous-module et le sous-espace de noms sont transposés en propriétés d'objet:

    var Counter;
    (function(Counter) {
        var Sub;
        (function(Sub) {
            var count = 0
            Sub.increase = function() {
                return ++count
            }
        })(Sub = Counter.Sub || (Counter.Sub = {}))
    })(Counter || (Counter = {}))

Le module et l'espace de noms TypeScript peuvent également être utilisés dans une instruction export:

    module Counter {
        let count = 0
        export module Sub {
            export const increase = () => ++count
        }
    }

    module Counter {
        let count = 0
        export namespace Sub {
            export const increase = () => ++count
        }
    }

Le code ci-dessus se traduit également en sous-module et sous-espace de noms:

    var Counter;
    (function(Counter) {
        var count = 0
        var Sub;
        (function(Sub) {
            Sub.increase = function() {
                return ++count
            }
        })(Sub = Counter.Sub || (Counter.Sub = {}))
    })(Counter || (Counter = {}))

Conclusion


Bienvenue dans JS, qui a plus de 10 systèmes / formats de modulation / espaces de noms:

  • Module IIFE: modèle de module JS
  • Module ouvert: modèle de module JS ouvert
  • Module CJS: module CommonJS ou module Node.js
  • Module AMD: définition de module asynchrone ou module RequireJS
  • Module UMD: définition de module universel ou module UmdJS
  • Module ES: module ECMAScript2015 ou ES6
  • Module dynamique ES: module dynamique ECMAScript2020 ou ES11
  • Module système: Module SystemJS
  • Module Webpack: compilation et assemblage des modules CJS, AMD et ES
  • Module Babel: transpilation du module ES
  • Module TypeScript et espace de noms

Heureusement, JS dispose actuellement d'outils standard intégrés pour travailler avec les modules pris en charge par Node.js et tous les navigateurs modernes. Pour les navigateurs plus anciens, vous pouvez utiliser la nouvelle syntaxe ES modulaire, la traduisant en syntaxe compatible à l'aide de Webpack / Babel / SystemJS / TypeScript.

Merci pour votre temps. J'espère que cela a été bien dépensé.

All Articles