Compreendendo (todos) formatos e ferramentas JavaScript "modulares"



Bom dia amigos

Apresento a você a tradução do artigo “Noções básicas sobre (todos) os formatos e ferramentas do módulo JavaScript” de Dixin.

Ao criar um aplicativo, geralmente existe o desejo de dividir o código em partes, blocos lógicos ou funcionais (módulos). No entanto, o JavaScript inicialmente não tinha suporte ao módulo. Isso levou ao surgimento de várias tecnologias modulares. Este artigo discute todos os conceitos básicos, modelos, bibliotecas, sintaxe e ferramentas para trabalhar com módulos em JavaScript.


Módulo IIFE: modelo de módulo JS


Ao definir uma variável em JS, nós a definimos como uma variável global. Isso significa que essa variável estará disponível em todos os arquivos JS carregados na página atual:

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

    //   
    increase()
    reset()

Para evitar a poluição do espaço para nome global, você pode usar uma função anônima:

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

Voila, não há mais variáveis ​​globais. No entanto, o código dentro da função não é executado.

IIFE: expressão imediata da função


Para executar o código dentro de uma função f, ela deve ser chamada usando ()como f(). Para executar código dentro de uma função anônima (() => {})também deve ser usado (). É assim (() => {})():

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

Isso é chamado de IIFE (imediatamente chamado de expressão de função). Um módulo pode ser definido da seguinte maneira:

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

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

Envolvemos o código do módulo em IIFE. Uma função anônima retorna um objeto. Isso substitui a interface de exportação. Existe apenas uma variável global - o nome do módulo (ou seu espaço para nome). Posteriormente, o nome do módulo pode ser usado para chamá-lo (exportação). Isso é chamado de modelo de módulo JS.

Impurezas de importação


Ao definir um módulo, algumas dependências podem ser necessárias. Ao usar um modelo modular, cada módulo dependente é uma variável global. Módulos dependentes podem ser definidos dentro de uma função anônima ou passados ​​para ela como argumentos:

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

Versões anteriores de bibliotecas populares, como o jQuery, usavam esse modelo (a versão mais recente do jQuery usa o módulo UMD).

Módulo aberto: modelo de módulo JS aberto


O modelo de módulo aberto foi criado por Christian Heilmann. Este modelo também é IIFE, mas a ênfase está na definição de todas as interfaces como variáveis ​​locais dentro de uma função anônima:

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

        return {
            increase,
            reset
        }
    })()

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

Essa sintaxe facilita a compreensão pelo que cada interface é responsável (ou pelo que faz).

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


CommonJS, originalmente chamado ServerJS, é um modelo para definir e usar módulos. Está embutido no Node.js. Por padrão, cada arquivo JS é um CJS. Variáveis moduletambém exportsfornecem exportação do módulo (arquivo). A função requirefornece carregamento e uso do módulo. O código a seguir demonstra a definição de um módulo de contador na sintaxe 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
    }

Aqui está como este módulo é usado:

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

No tempo de execução do Node.js. (mecanismo), esse modelo é usado envolvendo o código dentro do arquivo em uma função, para a qual variáveis exports, modulee uma função são passadas 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 ou módulo RequireJS


AMD ( definição de módulo assíncrono ) é um modelo para definir e usar módulos. É usado na biblioteca RequireJS . A AMD contém uma função de definedefinição de módulo que aceita o nome do módulo, nomes de dependência e função 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
        }
    })

Ele também contém uma função requirepara usar o módulo:

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


requireA AMD difere do requireCommonJS por considerar os nomes dos módulos e dos módulos como argumentos para a função.

Carregamento dinâmico


A função definetambém tem uma finalidade diferente. Ele pega uma função de retorno de chamada e passa uma requirefunção do tipo CommonJS para essa função. Dentro da função de retorno de chamada, require é chamado para carregar dinamicamente o 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 do módulo CommonJS


A função acima define, além disso require, pode levar as variáveis exportse como argumentos module. Portanto, o definecódigo do CommonJS pode ser executado 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: definição de módulo universal ou módulo UmdJS


UMD ( definição universal de módulo ) - um conjunto de modelos para garantir a operação do módulo em diferentes ambientes de tempo de execução.

UMD para AMD (RequireJS) e navegador


O código a seguir fornece o módulo na AMD (RequireJS) e no 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, mas é apenas IIFE. Uma função anônima determina se há uma função definedo AMD / RequireJS.

  • Se definedetectada, a função de fábrica é chamada através dela.
  • Se definenão encontrado, a função de fábrica é chamada diretamente. Neste ponto, o argumento rooté o objeto Window do navegador. Ele recebe módulos dependentes de variáveis ​​globais (propriedades do objeto Window). Quando um factorymódulo retorna, ele também se torna uma variável global (uma propriedade do objeto Window).

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


O código a seguir fornece o módulo no AMD (RequireJS) e no 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ão se assuste, é apenas IIFE novamente. Quando uma função anônima é chamada, seu argumento é “avaliado”. Avaliando o argumento para determinar o ambiente de execução (definido pela presença de variáveis modulee exportsdo CommonJS / Node.js e as funções definedo AMD / RequireJS).

  • Se o tempo de execução for CommonJS / Node.js, o argumento da função anônima criará a função manualmente define.
  • Se o tempo de execução for AMD / RequireJS, o argumento para a função anônima é uma função definedesse ambiente. A execução de uma função anônima garante que a função funcione define. Dentro de uma função anônima, uma função é chamada para criar o módulo define.

Módulo ES: módulo ECMAScript2015 ou ES6


Em 2015, a versão 6 da especificação JS introduziu uma nova sintaxe modular. Isso é chamado de ECMAScript 2015 (ES2015) ou ECMAScript 6 (ES6). A base da nova sintaxe são as palavras import- chave e export. O código a seguir demonstra o uso do módulo ES para importação / exportação nomeada e padrão (padrão):

    //  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 usar o arquivo de módulo no navegador, adicione a tag <script>e identificá-lo como um módulo: <script type="module" src="esCounterModule.js"></script>. Para usar este módulo no Node.js, altere sua extensão para .mjs:

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


Para compatibilidade com versões anteriores no navegador, você pode adicionar uma tag <script>com o atributo nomodule:

    <nomodule de script>
        alerta ('Não suportado')
    </script>

Módulo dinâmico ES: módulo dinâmico ECMAScript2020 ou ES11


A última versão 11 da especificação JS 2020 apresenta uma função interna importpara o uso dinâmico de módulos ES. Essa função retorna uma promessa, para que você possa usar o módulo com then:

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

Devido ao fato de a função importretornar uma promessa, ela pode usar a palavra await- chave :

    //   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 do Sistema: Módulo SystemJS


O SystemJS é uma biblioteca para suportar módulos ES em navegadores mais antigos. Por exemplo, o seguinte módulo é gravado usando a sintaxe 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 não funcionará em navegadores que não suportam a sintaxe ES6. Uma solução para esse problema é converter o código usando a System.registerinterface da 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
                })
            }
        }
    })

A nova sintaxe modular do ES6 desapareceu. Mas o código funcionará bem em navegadores mais antigos. Essa transpilação pode ser feita automaticamente usando Webpack, TypeScript, etc.

Carregamento dinâmico do módulo


O SystemJS também contém uma função importpara importação dinâmica:

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

Módulo Webpack: compilação e montagem de módulos CJS, AMD e ES


Webpack é um construtor de módulos. Seu transpiler combina os módulos CommonJS, AMD e ES em um único modelo modular balanceado e coleta todo o código em um único arquivo. Por exemplo, nos 3 arquivos a seguir, 3 módulos são definidos usando uma sintaxe 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
    }

O código a seguir demonstra o uso deste módulo:

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

O Webpack é capaz de combinar esses arquivos, apesar de serem sistemas modulares diferentes, em um arquivo main.js:

    raiz
        dist
            main.js (montagem de arquivos localizados na pasta src)
        src
            amdDependencyModule1.js
            commonJSDependencyModule2.js
            esCounterModule.js
            index.js
        webpack.config.js

Como o Webpack é baseado no Node.js, ele usa a sintaxe modular do CommonJS. Em 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 e construir, você deve executar os seguintes comandos:

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

Como resultado, o Webpack criará o arquivo main.js. O código a seguir está main.jsformatado para melhorar a legibilidade:

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

E, novamente, isso é apenas IIFE. O código de 4 arquivos é convertido em uma matriz de 4 funções. E essa matriz é passada para a função anônima como parâmetro.

Módulo Babel: transpilação do módulo ES


Babel é outro transportador para oferecer suporte ao código ES6 + em navegadores mais antigos. O módulo ES6 + acima pode ser convertido em um módulo Babel da seguinte maneira:

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

E aqui está o código index.js, demonstrando o uso deste 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 é a transpilação padrão. Babel também sabe como trabalhar com outras ferramentas.

Babel e SystemJS


O SystemJS pode ser usado como um plugin para Babel:

    Instalação do npm --save-dev @ babel / plugin-transform-modules-systemjs


Este plugin deve ser adicionado a babel.config.json:

    {
        'plugins': ['@ babel / plugin-transform-modules-systemjs'],
        'presets': [
            [
            '@ babel / env',
                {
                    'segmentações': {
                        'ie': '11'
                    }
                }
            ]
        ]
    }

Agora o Babel pode trabalhar com o SystemJS para transpilar os módulos CommonJS / Node.js, AMD / RequireJS e ES:

    npx babel src --out-dir lib

Resultado:

    raiz
        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

Toda a sintaxe dos módulos AMD, CommonJS e ES é transposta para a sintaxe 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: transpilação dos módulos CJS, AMD, ES e SystemJS


O TypeScript suporta todos os tipos de sintaxe JS, incluindo o ES6. Durante a transpilação, a sintaxe do módulo ES6 pode ser salva ou convertida para outro formato, incluindo CommonJS / Node.js, AMD / RequireJS, UMD / UmdJS ou SystemJS de acordo com as configurações da transpilação em tsconfig.json:

    {
        'compilerOptions': {
            'module': 'ES2020' // Nenhum, CommonJS, AMD, Sistema,
            UMD, ES6, ES2015, ESNext
        }
    }

Por exemplo:

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

A sintaxe modular do ES suportada pelo TypeScript é chamada de módulos externos.

Módulos internos e espaço para nome


TypeScript também possui palavras-chave modulee namespace. Eles são chamados de 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 são transpostos para objetos JS:

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

O módulo TypeScript e o namespace podem ter vários níveis de aninhamento por meio do separador .:

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

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

O submódulo e o sub namespace são transpostos para as propriedades do objeto:

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

O módulo TypeScript e o espaço para nome também podem ser usados ​​em uma instrução 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
        }
    }

O código acima também se traduz em submódulo e sub namespace:

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

Conclusão


Bem-vindo ao JS, que possui mais de 10 sistemas / formatos de modulação / espaços para nome:

  • Módulo IIFE: modelo de módulo JS
  • Módulo aberto: modelo de módulo JS aberto
  • Módulo CJS: módulo CommonJS ou módulo Node.js
  • Módulo AMD: definição de módulo assíncrono ou módulo RequireJS
  • Módulo UMD: definição de módulo universal ou módulo UmdJS
  • Módulo ES: módulo ECMAScript2015 ou ES6
  • Módulo dinâmico ES: módulo dinâmico ECMAScript2020 ou ES11
  • Módulo do Sistema: Módulo SystemJS
  • Módulo Webpack: compilação e montagem de módulos CJS, AMD e ES
  • Módulo Babel: transpilação do módulo ES
  • Módulo TypeScript e espaço para nome

Felizmente, o JS atualmente possui ferramentas internas padrão para trabalhar com módulos suportados pelo Node.js. e todos os navegadores modernos. Para navegadores mais antigos, você pode usar a nova sintaxe modular do ES, convertendo-a em sintaxe compatível usando Webpack / Babel / SystemJS / TypeScript.

Obrigado pelo seu tempo. Espero que tenha sido bem gasto.

All Articles