Comprender (todos) los formatos y herramientas de JavaScript "modulares"



¡Buen dia amigos!

Le presento la traducción del artículo "Entendiendo (todos) los formatos y herramientas del módulo JavaScript" de Dixin.

Al crear una aplicación, a menudo existe el deseo de dividir el código en partes, bloques lógicos o funcionales (módulos). Sin embargo, JavaScript inicialmente no tenía soporte para módulos. Esto ha llevado a la aparición de varias tecnologías modulares. Este artículo analiza todos los conceptos básicos, plantillas, bibliotecas, sintaxis y herramientas para trabajar con módulos en JavaScript.


Módulo IIFE: plantilla de módulo JS


Al definir una variable en JS, la definimos como una variable global. Esto significa que dicha variable estará disponible en todos los archivos JS cargados en la página actual:

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

    //   
    increase()
    reset()

Para evitar la contaminación del espacio de nombres global, puede usar una función anónima:

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

Voila, no hay más variables globales. Sin embargo, el código dentro de la función no se ejecuta.

IIFE: expresión de función inmediata


Para ejecutar código dentro de una función f, debe llamarse usando ()how f(). Para ejecutar código dentro de una función anónima (() => {})también se debe utilizar (). Se ve así (() => {})():

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

Esto se llama IIFE (inmediatamente llamado expresión de función). Un módulo se puede definir de la siguiente manera:

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

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

Envolvemos el código del módulo en IIFE. Una función anónima devuelve un objeto. Esto reemplaza la interfaz de exportación. Solo hay una variable global: el nombre del módulo (o su espacio de nombres). Posteriormente, el nombre del módulo se puede usar para llamarlo (exportar). Esto se llama la plantilla del módulo JS.

Impurezas de importación


Al definir un módulo, pueden ser necesarias algunas dependencias. Cuando se usa una plantilla modular, cada módulo dependiente es una variable global. Los módulos dependientes se pueden definir dentro de una función anónima o pasarle como argumentos:

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

Las versiones anteriores de bibliotecas populares como jQuery usaban esta plantilla (la última versión de jQuery usa el módulo UMD).

Módulo abierto: plantilla de módulo JS abierto


La plantilla de módulo abierto fue acuñada por Christian Heilmann. Esta plantilla también es IIFE, pero el énfasis está en definir todas las interfaces como variables locales dentro de una función anónima:

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

        return {
            increase,
            reset
        }
    })()

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

Esta sintaxis hace que sea más fácil entender de qué es responsable cada interfaz (o qué hace).

Módulo CJS: módulo CommonJS o módulo Node.js


CommonJS, originalmente llamado ServerJS, es una plantilla para definir y usar módulos. Está integrado en Node.js. Por defecto, cada archivo JS es un CJS. Las variables moduletambién exportsproporcionan la exportación del módulo (archivo). La función requireproporciona carga y uso del módulo. El siguiente código muestra la definición de un módulo contador en la sintaxis 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
    }

Así es como se usa este módulo:

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

En el tiempo de ejecución de Node.js (motor), esta plantilla se usa envolviendo el código dentro del archivo en una función, a la que se pasan variables exports, moduley una función como parámetros 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)

Módulo AMD o módulo RequireJS


AMD ( definición de módulo asíncrono ) es una plantilla para definir y usar módulos. Se usa en la biblioteca RequireJS . AMD contiene una función de definedefinición de módulo que acepta el nombre del módulo, los nombres de dependencia y la función de fábrica:

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

        return {
            increase,
            reset
        }
    })

También contiene una función requirepara usar el módulo:

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


requireAMD difiere de requireCommonJS en que toma los nombres de los módulos y los módulos mismos como argumentos de la función.

Carga dinámica


La función definetambién tiene un propósito diferente. Toma una función de devolución de llamada y pasa una requirefunción similar a CommonJS a esta función. Dentro de la función de devolución de llamada, se llama a require para cargar dinámicamente el módulo:

    //   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
        }
    })

Módulo AMD del módulo CommonJS


La función anterior define, además require, puede tomar las variables exportsy como argumentos module. Por lo tanto, el definecódigo de CommonJS se puede ejecutar dentro de :

    //  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()
    })

Módulo UMD: definición de módulo universal o módulo UmdJS


UMD ( definición de módulo universal ): un conjunto de plantillas para garantizar el funcionamiento del módulo en diferentes entornos de tiempo de ejecución.

UMD para AMD (RequireJS) y navegador


El siguiente código proporciona el módulo tanto en AMD (RequireJS) como en el navegador:

    //  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
        }
    })

Parece complicado, pero es solo IIFE. Una función anónima determina si hay una función definede AMD / RequireJS.

  • Si se definedetecta, la función de fábrica se llama a través de ella.
  • Si defineno se encuentra, la función de fábrica se llama directamente. En este punto, el argumento rootes el objeto de la ventana del navegador. Recibe módulos dependientes de variables globales (propiedades del objeto Window). Cuando un factorymódulo regresa, también se convierte en una variable global (una propiedad del objeto Window).

UMD para AMD (RequireJS) y CommonJS (Node.js)


El siguiente código proporciona el módulo tanto en AMD (RequireJS) como en 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)

No tengas miedo, es solo IIFE nuevamente. Cuando se llama a una función anónima, su argumento se "evalúa". Evaluar el argumento para determinar el entorno de ejecución (definido por la presencia de variables moduley exportsde CommonJS / Node.js, y las funciones definede AMD / RequireJS).

  • Si el tiempo de ejecución es CommonJS / Node.js, el argumento de la función anónima crea manualmente la función define.
  • Si el tiempo de ejecución es AMD / RequireJS, el argumento de la función anónima es una función definede ese entorno. Realizar una función anónima asegura que la función funcione define. Dentro de una función anónima, se llama a una función para crear el módulo define.

Módulo ES: módulo ECMAScript2015 o ES6


En 2015, la versión 6 de la especificación JS introdujo una nueva sintaxis modular. Esto se llama ECMAScript 2015 (ES2015) o ECMAScript 6 (ES6). La base de la nueva sintaxis son las palabras clave importy export. El siguiente código demuestra el uso del módulo ES para la importación / exportación con nombre y predeterminada (predeterminada):

    //  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
    }

Para utilizar el archivo de módulo en el navegador, agregar la etiqueta <script>e identificarlo como un módulo: <script type="module" src="esCounterModule.js"></script>. Para usar este módulo en Node.js, cambie su extensión a .mjs:

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


Para la compatibilidad con versiones anteriores en el navegador, puede agregar una etiqueta <script>con el atributo nomodule:

    <script nomodule>
        alerta ('No compatible')
    </script>

Módulo dinámico ES: módulo dinámico ECMAScript2020 o ES11


La última versión 11 de la especificación JS 2020 presenta una función incorporada importpara usar dinámicamente módulos ES. Esta función devuelve una promesa, por lo que puede usar el módulo con then:

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

Debido al hecho de que la función importdevuelve una promesa, puede usar la palabra clave await:

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

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

Módulo del sistema: Módulo SystemJS


SystemJS es una biblioteca para admitir módulos ES en navegadores antiguos. Por ejemplo, el siguiente módulo está escrito usando la sintaxis 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
    }

Este código no funcionará en navegadores que no admitan la sintaxis de ES6. Una solución a este problema es traducir el código usando la System.registerinterfaz de la biblioteca 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 nueva sintaxis modular ES6 ha desaparecido. Pero el código funcionará bien en navegadores antiguos. Esta transpilación se puede hacer automáticamente usando Webpack, TypeScript, etc.

Módulo dinámico de carga


SystemJS también contiene una función importpara la importación dinámica:

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

Módulo webpack: compilación y montaje de módulos CJS, AMD y ES


Webpack es un generador de módulos. Su transpiler combina los módulos CommonJS, AMD y ES en una única plantilla modular equilibrada y recopila todo el código en un solo archivo. Por ejemplo, en los siguientes 3 archivos, 3 módulos se definen usando una sintaxis diferente:

    //  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
    }

El siguiente código demuestra el uso de este módulo:

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

Webpack puede combinar estos archivos, a pesar del hecho de que son sistemas modulares diferentes, en un solo archivo main.js:

    raíz
        dist
            main.js (ensamblaje de archivos ubicados en la carpeta src)
        src
            amdDependencyModule1.js
            commonJSDependencyModule2.js
            esCounterModule.js
            index.js
        webpack.config.js

Dado que Webpack se basa en Node.js, utiliza la sintaxis modular de CommonJS. En webpack.config.js:

    const path = require('path')

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

Para compilar y compilar, debe ejecutar los siguientes comandos:

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

Como resultado, Webpack creará el archivo main.js. El siguiente código está main.jsformateado para mejorar la legibilidad:

    (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
        }
    ])

Y de nuevo, esto es solo IIFE. El código de 4 archivos se convierte en una matriz de 4 funciones. Y esta matriz se pasa a la función anónima como un parámetro.

Módulo Babel: transpilación del módulo ES


Babel es otro transportador para soportar código ES6 + en navegadores antiguos. El módulo ES6 + anterior se puede convertir en un módulo Babel de la siguiente manera:

    // 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
    }

Y aquí está el código index.js, que demuestra el uso de este módulo:

    // 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()

Esta es la transpilación predeterminada. Babel también sabe cómo trabajar con otras herramientas.

Babel y SystemJS


SystemJS se puede usar como complemento para Babel:

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


Este complemento se debe agregar a babel.config.json:

    {
        'plugins': ['@ babel / plugin-transform-modules-systemjs'],
        'Preajustes': [
            [
            '@ babel / env',
                {
                    'objetivos': {
                        'ie': '11'
                    }
                }
            ]
        ]
    }

Ahora Babel puede trabajar con SystemJS para transpilar módulos CommonJS / Node.js, AMD / RequireJS y ES:

    npx babel src --out-dir lib

Resultado:

    raíz
        lib
            amdDependencyModule1.js (transpilado usando SystemJS)
            commonJSDependencyModule2.js (transpilado usando SystemJS)
            esCounterModule.js (transpilado usando SystemJS)
            index.js (transpilado usando SystemJS)
        src
            amdDependencyModule1.js
            commonJSDependencyModule2.js
            esCounterModule.js
            index.js
        babel.config.json

La sintaxis completa de los módulos AMD, CommonJS y ES se transpone a la sintaxis de 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()
            }
        }
    })

Módulo TypeScript: transpilación de módulos CJS, AMD, ES y SystemJS


TypeScript admite todos los tipos de sintaxis JS, incluido ES6. Durante la transpilación, la sintaxis del módulo ES6 se puede guardar o convertir a otro formato, incluidos CommonJS / Node.js, AMD / RequireJS, UMD / UmdJS o SystemJS de acuerdo con la configuración de la transpilación en tsconfig.json:

    {
        'compilerOptions': {
            'module': 'ES2020' // Ninguno, CommonJS, AMD, System,
            UMD, ES6, ES2015, ESSiguiente
        }
    }

Por ejemplo:

    // 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 sintaxis modular de ES compatible con TypeScript se denomina módulos externos.

Módulos internos y espacio de nombres


TypeScript también tiene palabras clave moduley namespace. Se llaman módulos internos:

    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(' .')
        }
    }

Ambos se transponen en objetos JS:

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

El módulo TypeScript y el espacio de nombres pueden tener varios niveles de anidamiento a través del separador .:

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

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

El submódulo y el subespacio de nombres se transponen en propiedades de objeto:

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

El módulo TypeScript y el espacio de nombres también se pueden usar en una declaración 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
        }
    }

El código anterior también se traduce en submódulo y subespacio de nombres:

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

Conclusión


Bienvenido a JS, que tiene más de 10 sistemas / formatos de modulación / espacios de nombres:

  • Módulo IIFE: plantilla de módulo JS
  • Módulo abierto: plantilla de módulo JS abierto
  • Módulo CJS: módulo CommonJS o módulo Node.js
  • Módulo AMD: definición de módulo asíncrono o módulo RequireJS
  • Módulo UMD: definición de módulo universal o módulo UmdJS
  • Módulo ES: módulo ECMAScript2015 o ES6
  • Módulo dinámico ES: módulo dinámico ECMAScript2020 o ES11
  • Módulo del sistema: Módulo SystemJS
  • Módulo webpack: compilación y montaje de módulos CJS, AMD y ES
  • Módulo Babel: transpilación del módulo ES
  • Módulo TypeScript y espacio de nombres

Afortunadamente, JS actualmente tiene herramientas integradas estándar para trabajar con módulos compatibles con Node.js y todos los navegadores modernos. Para navegadores antiguos, puede usar la nueva sintaxis modular ES, traduciéndola a una sintaxis compatible usando Webpack / Babel / SystemJS / TypeScript.

Gracias por tu tiempo. Espero que haya sido bien aprovechado.

All Articles