了解(全部)JavaScript模块格式和工具

目录 (Table of Contents)

IIFE模块:JavaScript模块模式 (IIFE Module: JavaScript Module Pattern)

In the browser, defining a JavaScript variable is defining a global variable, which causes pollution across all JavaScript files loaded by the current web page:

在浏览器中,定义JavaScript变量就是定义全局变量,这会导致当前网页加载的所有JavaScript文件受到污染:

// Define global variables.
let count = 0;
const increase = () => ++count;
const reset = () => {
    count = 0;
    console.log("Count is reset.");
};

// Use global variables.
increase();
reset();

To avoid global pollution, an anonymous function can be used to wrap the code:

为了避免全球污染,可以使用匿名函数来包装代码:

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

Apparently, there is no longer any global variable. However, defining a function does not execute the code inside the function.

显然,不再有任何全局变量。 但是,定义函数不会在函数内部执行代码。

IIFE:立即调用函数表达式 (IIFE: Immediately Invoked Function Expression)

To execute the code inside a function f, the syntax is function call () as f(). To execute the code inside an anonymous function (() => {}), the same function call syntax () can be used as (() => {})():

要在函数f执行代码,语法为function call () as f() 。 为了执行一个匿名函数内的代码(() => {})同样的函数调用的语法()可以被用作(() => {})()

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

This is called an IIFE (Immediately invoked function expression). So a basic module can be defined in this way:

这称为IIFE(立即调用的函数表达式)。 因此,可以通过以下方式定义基本模块:

// Define IIFE module.
const iifeCounterModule = (() => {
    let count = 0;
    return {
        increase: () => ++count,
        reset: () => {
            count = 0;
            console.log("Count is reset.");
        }
    };
})();

// Use IIFE module.
iifeCounterModule.increase();
iifeCounterModule.reset();

It wraps the module code inside an IIFE. The anonymous function returns an object, which is the placeholder of exported APIs. Only 1 global variable is introduced, which is the module name (or namespace). Later, the module name can be used to call the exported module APIs. This is called the module pattern of JavaScript.

它将模块代码包装在IIFE中。 匿名函数返回一个对象,该对象是导出的API的占位符。 仅引入了1个全局变量,即模块名称(或名称空间)。 以后,可以使用模块名称来调用导出的模块API。 这称为JavaScript的模块模式。

导入mixins (Import mixins)

When defining a module, some dependencies may be required. With IIFE module pattern, each dependent module is a global variable. The dependent modules can be directly accessed inside the anonymous function, or they can be passed as the anonymous function’s arguments:

定义模块时,可能需要一些依赖关系。 使用IIFE模块模式,每个从属模块都是全局变量。 依赖模块可以直接在匿名函数内部访问,也可以作为匿名函数的参数传递:

// Define IIFE module with dependencies.
const iifeCounterModule = ((dependencyModule1, dependencyModule2) => {
    let count = 0;
    return {
        increase: () => ++count,
        reset: () => {
            count = 0;
            console.log("Count is reset.");
        }
    };
})(dependencyModule1, dependencyModule2);

The early version of popular libraries, like jQuery, followed this pattern. (The latest version of jQuery follows the UMD module, which is explained later in this article.)

流行的库(如jQuery)的早期版本遵循这种模式。 (最新版本的jQuery遵循UMD模块,本文稍后将对此进行说明。)

显示模块:JavaScript显示模块模式 (Revealing Module: JavaScript Revealing Module Pattern)

The revealing module pattern is named by Christian Heilmann. This pattern is also an IIFE, but it emphasizes defining all APIs as local variables inside the anonymous function:

显示模块模式由Christian Heilmann命名。 此模式也是IIFE,但它强调将所有API定义为匿名函数内的局部变量:

// Define revealing module.
const revealingCounterModule = (() => {
    let count = 0;
    const increase = () => ++count;
    const reset = () => {
        count = 0;
        console.log("Count is reset.");
    };

    return {
        increase,
        reset
    };
})();

// Use revealing module.
revealingCounterModule.increase();
revealingCounterModule.reset();

With this syntax, it becomes easier when the APIs need to call each other.

使用这种语法,当API需要相互调用时,将变得更加容易。

CJS模块:CommonJS模块或Node.js模块 (CJS Module: CommonJS Module, or Node.js Module)

CommonJS, initially named ServerJS, is a pattern to define and consume modules. It is implemented by Node,js. By default, each .js file is a CommonJS module. A module variable and an exports variable are provided for a module (a file) to expose APIs. And a require function is provided to load and consume a module. The following code defines the counter module in CommonJS syntax:

CommonJS(最初名为ServerJS)是定义和使用模块的模式。 它由Node.js实现。 默认情况下,每个.js文件都是CommonJS模块。 为模块(文件)提供了module变量和exports变量以公开API。 并且提供了require函数来加载和使用模块。 以下代码以CommonJS语法定义了计数器模块:

// Define CommonJS module: commonJSCounterModule.js.
const dependencyModule1 = require("./dependencyModule1");
const dependencyModule2 = require("./dependencyModule2");

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

exports.increase = increase;
exports.reset = reset;
// Or equivalently:
module.exports = {
    increase,
    reset
};

The following example consumes the counter module:

以下示例使用了计数器模块:

// Use CommonJS module.
const { increase, reset } = require("./commonJSCounterModule");
increase();
reset();
// Or equivalently:
const commonJSCounterModule = require("./commonJSCounterModule");
commonJSCounterModule.increase();
commonJSCounterModule.reset();

At runtime, Node.js implements this by wrapping the code inside the file into a function, then passes the exports variable, module variable, and require function through arguments.

在运行时,Node.js通过将文件内的代码包装到一个函数中,然后通过参数传递exports变量, module变量和require函数来实现此目的。

// Define CommonJS module: wrapped commonJSCounterModule.js.
(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("Count is reset.");
    };

    module.exports = {
        increase,
        reset
    };

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

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

AMD模块:异步模块定义或RequireJS模块 (AMD Module: Asynchronous Module Definition, or RequireJS Module)

AMD (Asynchronous Module Definition), is a pattern to define and consume module. It is implemented by RequireJS library. AMD provides a define function to define module, which accepts the module name, dependent modules’ names, and a factory function:

AMD( 异步模块定义 )是一种定义和使用模块的模式。 它由RequireJS库实现。 AMD提供了一个define模块的define函数,该函数接受模块名称,从属模块的名称以及工厂功能:

// Define AMD module.
define("amdCounterModule", ["dependencyModule1", "dependencyModule2"], 
      (dependencyModule1, dependencyModule2) => {
    let count = 0;
    const increase = () => ++count;
    const reset = () => {
        count = 0;
        console.log("Count is reset.");
    };

    return {
        increase,
        reset
    };
});

It also provides a require function to consume module:

它还提供了一个require函数来使用模块:

// Use AMD module.
require(["amdCounterModule"], amdCounterModule => {
    amdCounterModule.increase();
    amdCounterModule.reset();
});

The AMD require function is totally different from the CommonJS require function. AMD require accepts the names of modules to be consumed, and passes the module to a function argument.

AMD require函数与CommonJS require函数完全不同。 AMD require接受要使用的模块的名称,并将模块传递给函数参数。

动态加载 (Dynamic Loading)

AMD’s define function has another overload. It accepts a callback function, and passes a CommonJS-like require function to that callback. Inside the callback function, require can be called to dynamically load the module:

AMD的define函数还有另一个重载。 它接受一个回调函数,并将类似于CommonJS的require函数传递给该回调。 在回调函数中,可以调用require以动态加载模块:

// Use dynamic AMD module.
define(require => {
    const dynamicDependencyModule1 = require("dependencyModule1");
    const dynamicDependencyModule2 = require("dependencyModule2");

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

    return {
        increase,
        reset
    };
});

CommonJS模块的AMD模块 (AMD Module from CommonJS Module)

The above define function overload can also pass the require function as well as exports variable and module to its callback function. So inside the callback, CommonJS syntax code can work:

上面的define函数重载还可以传递require函数以及将变量和module exports到其回调函数。 因此,在回调内部,CommonJS语法代码可以正常工作:

// Define AMD module with CommonJS code.
define((require, exports, module) => {
    // CommonJS code.
    const dependencyModule1 = require("dependencyModule1");
    const dependencyModule2 = require("dependencyModule2");

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

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

// Use AMD module with CommonJS code.
define(require => {
    // CommonJS code.
    const counterModule = require("amdCounterModule");
    counterModule.increase();
    counterModule.reset();
});

UMD模块:通用模块定义或UmdJS模块 (UMD Module: Universal Module Definition, or UmdJS Module)

UMD (Universal Module Definition) is a set of tricky patterns to make your code file work in multiple environments.

UMD( 通用模块定义 )是一组棘手的模式,可以使您的代码文件在多种环境中工作。

适用于AMD(RequireJS)和本机浏览器的UMD (UMD for Both AMD (RequireJS) and Native Browser)

For example, the following is a kind of UMD pattern to make module definition work with both AMD (RequireJS) and native browser:

例如,以下是一种UMD模式,以使模块定义可用于AMD(RequireJS)和本机浏览器:

// Define UMD module for both AMD and browser.
((root, factory) => {
    // Detects AMD/RequireJS"s define function.
    if (typeof define === "function" && define.amd) {
        // Is AMD/RequireJS. Call factory with AMD/RequireJS"s define function.
        define("umdCounterModule", ["deependencyModule1", "dependencyModule2"], factory);
    } else {
        // Is Browser. Directly call factory.
        // Imported dependencies are global variables(properties of window object).
        // Exported module is also a global variable(property of window object)
        root.umdCounterModule = factory(root.deependencyModule1, root.dependencyModule2);
    }
})(typeof self !== "undefined" ? self : this, (deependencyModule1, dependencyModule2) => {
    // Module code goes here.
    let count = 0;
    const increase = () => ++count;
    const reset = () => {
        count = 0;
        console.log("Count is reset.");
    };

    return {
        increase,
        reset
    };
});

It is more complex but it is just an IIFE. The anonymous function detects if AMD’s define function exists.

它比较复杂,但是它只是一个IIFE。 匿名函数检测AMD的define函数是否存在。

  • If yes, call the module factory with AMD’s define function.

    如果是,请使用AMD的define函数致电模块工厂。

  • If not, it calls the module factory directly. At this moment, the root argument is actually the browser’s window object. It gets dependency modules from global variables (properties of window object). When factory returns the module, the returned module is also assigned to a global variable (property of window object).

    如果不是,它将直接调用模块工厂。 此时, root参数实际上是浏览器的window对象。 它从全局变量( window对象的属性)获取依赖项模块。 当factory返回模块时,返回的模块也被分配给全局变量( window对象的属性)。

适用于AMD(RequireJS)和CommonJS(Node.js)的UMD (UMD for Both AMD (RequireJS) and CommonJS (Node.js))

The following is another kind of UMD pattern to make module definition work with both AMD (RequireJS) and CommonJS (Node.js):

以下是使模块定义与AMD(RequireJS)和CommonJS(Node.js)一起工作的另一种UMD模式:

(define => define((require, exports, module) => {
    // Module code goes here.
    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
    };
}))(// Detects module variable and exports variable of CommonJS/Node.js.
    // Also detect the define function of AMD/RequireJS.
    typeof module === "object" && module.exports && typeof define !== "function"
        ? // Is CommonJS/Node.js. Manually create a define function.
            factory => module.exports = factory(require, exports, module)
        : // Is AMD/RequireJS. Directly use its define function.
            define);

Again, don’t be scared. It is just another IIFE. When the anonymous function is called, its argument is evaluated. The argument evaluation detects the environment (check the module variable and exports variable of CommonJS/Node.js, as well as the define function of AMD/RequireJS).

同样,不要害怕。 这只是另一个IIFE。 调用匿名函数时,将评估其参数。 参数评估检测环境(检查CommonJS / Node.js的模块变量和exports变量,以及AMD / RequireJS的define函数)。

  • If the environment is CommonJS/Node.js, the anonymous function’s argument is a manually created define function.

    如果环境是CommonJS / Node.js,则匿名函数的参数是手动创建的define函数。

  • If the environment is AMD/RequireJS, the anonymous function’s argument is just AMD’s define function. So when the anonymous function is executed, it is guaranteed to have a working define function. Inside the anonymous function, it simply calls the define function to create the module.

    如果环境是AMD / RequireJS,则匿名函数的参数就是AMD的define函数。 因此,当执行匿名函数时,可以确保它具有有效的define函数。 在匿名函数内部,它仅调用define函数来创建模块。

ES模块:ECMAScript 2015或ES6模块 (ES Module: ECMAScript 2015, or ES6 Module)

After all the module mess, in 2015, JavaScript’s spec version 6 introduces one more different module syntax. This spec is called ECMAScript 2015 (ES2015), or ECMAScript 6 (ES6). The main syntax is the import keyword and the export keyword. The following example uses new syntax to demonstrate ES module’s named import/export and default import/export:

在所有模块混乱之后,在2015年,JavaScript的规范版本6引入了另一种不同的模块语法。 该规范称为ECMAScript 2015(ES2015)或ECMAScript 6(ES6)。 主要语法是import关键字和export关键字。 以下示例使用新语法演示ES模块的命名import / export和默认import / export

// Define ES module: esCounterModule.js or esCounterModule.mjs.
import dependencyModule1 from "./dependencyModule1.mjs";
import dependencyModule2 from "./dependencyModule2.mjs";

let count = 0;
// Named export:
export const increase = () => ++count;
export const reset = () => {
    count = 0;
    console.log("Count is reset.");
};
// Or default export:
export default {
    increase,
    reset
};

To use this module file in browser, add a <script> tag and specify it is a module: <script type="module" src="esCounterModule.js"></script>. To use this module file in Node.js, rename its extension from .js to .mjs.

要在浏览器中使用此模块文件,请添加<script>标记并将其指定为模块: <script type="module" src="esCounterModule.js"></script> 。 要在Node.js中使用此模块文件, 请将其扩展名从.js重命名为.mjs

// Use ES module.
// Browser: <script type="module" src="esCounterModule.js"></script> or inline.
// Server: esCounterModule.mjs
// Import from named export.
import { increase, reset } from "./esCounterModule.mjs";
increase();
reset();
// Or import from default export:
import esCounterModule from "./esCounterModule.mjs";
esCounterModule.increase();
esCounterModule.reset();

For browser, <script>’s nomodule attribute can be used for fallback:

对于浏览器, <script>nomodule属性可用于回退:

<script nomodule>
    alert("Not supported.");
</script>

ES动态模块:ECMAScript 2020或ES11动态模块 (ES Dynamic Module: ECMAScript 2020, or ES11 Dynamic Module)

In 2020, the latest JavaScript spec version 11 is introducing a built-in function import to consume an ES module dynamically. The import function returns a promise, so its then method can be called to consume the module:

2020年,最新JavaScript规范版本11引入了内置函数import以动态使用ES模块。 import函数返回一个promise ,因此可以调用其then方法来消耗模块:

// Use dynamic ES module with promise APIs, import from named export:
import("./esCounterModule.js").then(({ increase, reset }) => {
    increase();
    reset();
});
// Or import from default export:
import("./esCounterModule.js").then(dynamicESCounterModule => {
    dynamicESCounterModule.increase();
    dynamicESCounterModule.reset();
});

By returning a promise, apparently, import function can also work with the await keyword:

通过返回promise ,显然, import功能也可以与await关键字一起使用:

// Use dynamic ES module with async/await.
(async () => {

    // Import from named export:
    const { increase, reset } = await import("./esCounterModule.js");
    increase();
    reset();

    // Or import from default export:
    const dynamicESCounterModule = await import("./esCounterModule.js");
    dynamicESCounterModule.increase();
    dynamicESCounterModule.reset();

})();

The following is the compatibility of import/dynamic import/export, from this link:

以下是从此链接导入/动态导入/导出的兼容性:

import compatibility

export compatibility

系统模块:SystemJS模块 (System Module: SystemJS Module)

SystemJS is a library that can enable ES module syntax for older ES. For example, the following module is defined in ES 6syntax:

SystemJS是一个库,可以为较旧的ES启用ES模块语法。 例如,ES 6语法中定义了以下模块:

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

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

If the current runtime, like an old browser, does not support ES6 syntax, the above code cannot work. One solution is to transpile the above module definition to a call of SystemJS library API, System.register:

如果当前运行时(例如旧的浏览器)不支持ES6语法,则以上代码将无法运行。 一种解决方案是将上述模块定义转换为对SystemJS库API System.register

// Define SystemJS module.
System.register(["./dependencyModule1.js", "./dependencyModule2.js"], 
                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;
            // Named export:
            exports_1("increase", increase = function () { return ++count };
            exports_1("reset", reset = function () {
                count = 0;
                console.log("Count is reset.");
            };);
            // Or default export:
            exports_1("default", {
                increase,
                reset
            });
        }
    };
});

So that the import/export new ES6 syntax is gone. The old API call syntax works for sure. This transpilation can be done automatically with Webpack, TypeScript, etc., which are explained later in this article.

这样import / export新的ES6语法就消失了。 旧的API调用语法肯定可以正常工作。 可以使用Webpack,TypeScript等自动完成此转换,本文稍后将对此进行说明。

动态模块加载 (Dynamic Module Loading)

SystemJS also provides an import function for dynamic import:

SystemJS还提供了用于动态导入的导入功能:

// Use SystemJS module with promise APIs.
System.import("./esCounterModule.js").then(dynamicESCounterModule => {
    dynamicESCounterModule.increase();
    dynamicESCounterModule.reset();
});

Webpack模块:来自CJS,AMD,ES模块的捆绑包 (Webpack Module: Bundle from CJS, AMD, ES Modules)

Webpack is a bundler for modules. It transpiles combined CommonJS module, AMD module, and ES module into a single harmony module pattern, and bundle all code into a single file. For example, the following three files define three modules in three different syntaxes:

Webpack是模块的捆绑器。 它将组合的CommonJS模块,AMD模块和ES模块转换为单个和声模块模式,并将所有代码捆绑为一个文件。 例如,以下三个文件以三种不同的语法定义了三个模块:

// Define AMD module: amdDependencyModule1.js
define("amdDependencyModule1", () => {
    const api1 = () => { };
    return {
        api1
    };
});

// Define CommonJS module: commonJSDependencyModule2.js
const dependencyModule1 = require("./amdDependencyModule1");
const api2 = () => dependencyModule1.api1();
exports.api2 = api2;

// Define ES module: esCounterModule.js.
import dependencyModule1 from "./amdDependencyModule1";
import dependencyModule2 from "./commonJSDependencyModule2";
dependencyModule1.api1();
dependencyModule2.api2();

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

export default {
    increase,
    reset
}

And the following file consumes the counter module:

并且以下文件占用了计数器模块:

// Use ES module: index.js
import counterModule from "./esCounterModule";
counterModule.increase();
counterModule.reset();

Webpack can bundle all the above files, even they are in 3 different module systems, into a single file main.js:

Webpack可以将上述所有文件打包在一起,即使它们位于3个不同的模块系统中,也都打包到一个文件main.js中

      • main.js (Bundle of all files under src)

        main.js ( src下所有文件的捆绑)

      dist

      dist

      • amdDependencyModule1.js

        amdDependencyModule1.js

      • commonJSDependencyModule2.js

        commonJSDependencyModule2.js

      • esCounterModule.js

        esCounterModule.js

      • index.js

        index.js

      src

      src

    • webpack.config.js

      webpack.config.js

    root

Since Webpack is based on Node.js, Webpack uses CommonJS module syntax for itself. In webpack.config.js:

由于Webpack基于Node.js,因此Webpack本身使用CommonJS模块语法。 在webpack.config.js中

const path = require('path');

module.exports = {
    entry: './src/index.js',
    mode: "none", // Do not optimize or minimize the code for readability.
    output: {
        filename: 'main.js',
        path: path.resolve(__dirname, 'dist'),
    },
};

Now run the following command to transpile and bundle all four files, which are in different syntax:

现在运行以下命令来转换和捆绑所有四个文件,它们的语法不同:

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

AS a result, Webpack generates the bundle file main.js. The following code in main.js is reformatted, and variables are renamed, to improve readability:

结果,Webpack生成了捆绑文件main.js。 重新格式化main.js中的以下代码, 并重命名变量,以提高可读性:

(function (modules) { // webpackBootstrap
    // The module cache
    var installedModules = {};
    // The require function
    function require(moduleId) {
        // Check if module is in cache
        if (installedModules[moduleId]) {
            return installedModules[moduleId].exports;

        }
        // Create a new module (and put it into the cache)
        var module = installedModules[moduleId] = {
            i: moduleId,
            l: false,
            exports: {}

        };
        // Execute the module function
        modules[moduleId].call(module.exports, module, module.exports, require);
        // Flag the module as loaded
        module.l = true;
        // Return the exports of the module
        return module.exports;
    }

    // expose the modules object (__webpack_modules__)
    require.m = modules;
    // expose the module cache
    require.c = installedModules;
    // define getter function for harmony exports
    require.d = function (exports, name, getter) {
        if (!require.o(exports, name)) {
            Object.defineProperty(exports, name, { enumerable: true, get: getter });
        }
    };
    // define __esModule on exports
    require.r = function (exports) {
        if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
            Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });

        }
        Object.defineProperty(exports, '__esModule', { value: true });
    };
    // create a fake namespace object
    // mode & 1: value is a module id, require it
    // mode & 2: merge all properties of value into the ns
    // mode & 4: return value when already ns object
    // mode & 8|1: behave like 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 function for compatibility with non-harmony modules
    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 = "";
    // Load entry module and return exports
    return require(require.s = 0);
})([
    function (module, exports, require) {
        "use strict";
        require.r(exports);
        // Use ES module: index.js.
        var esCounterModule = require(1);
        esCounterModule["default"].increase();
        esCounterModule["default"].reset();
    },
    function (module, exports, require) {
        "use strict";
        require.r(exports);
        // Define ES module: 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("Count is reset.");
        };

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

Again, it is just another IIFE. The code of all four files is transpiled to the code in four functions in an array. And that array is passed to the anonymous function as an argument.

同样,这只是另一个IIFE。 所有四个文件的代码都将转换为数组中四个函数的代码。 然后将该数组作为参数传递给匿名函数。

Babel模块:从ES模块转换 (Babel Module: Transpile from ES Module)

Babel is another transpiler to convert ES6+ JavaScript code to the older syntax for the older environment like older browsers. The above counter module in ES6 import/export syntax can be converted to the following babel module with new syntax replaced:

Babel是另一个将旧版环境(如旧版浏览器)的ES6 + JavaScript代码转换为旧版语法的编译器。 可以将ES6导入/导出语法中的上述计数器模块转换为以下替换了新语法的babel模块:

// Babel.
Object.defineProperty(exports, "__esModule", {
    value: true
});
exports["default"] = void 0;
function _interopRequireDefault(obj) 
         { return obj && obj.__esModule ? obj : { "default": obj }; }

// Define ES module: 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("Count is reset.");
};

exports["default"] = {
    increase: increase,
    reset: reset
};

And here is the code in index.js which consumes the counter module:

这是index.js中使用计数器模块的代码:

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

// Use ES module: index.js
var esCounterModule = _interopRequireDefault(require("./esCounterModule.js"));
esCounterModule["default"].increase();
esCounterModule["default"].reset();

This is the default transpilation. Babel can also work with other tools.

这是默认的转译。 Babel还可以与其他工具一起使用。

Babel与SystemJS (Babel with SystemJS)

SystemJS can be used as a plugin for Babel:

SystemJS可以用作Babel的插件:

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

And it should be added to the Babel configuration babel.config.json:

并且应该将其添加到Babel配置babel.config.json中

{
    "plugins": ["@babel/plugin-transform-modules-systemjs"],
    "presets": [
        [
            "@babel/env",
            {
                "targets": {
                    "ie": "11"
                }
            }
        ]
    ]
}

Now Babel can work with SystemJS to transpile CommonJS/Node.js module, AMD/RequireJS module, and ES module:

现在,Babel可以与SystemJS一起移植CommonJS / Node.js模块,AMD / RequireJS模块和ES模块:

npx babel src --out-dir lib

The result is:

结果是:

      • amdDependencyModule1.js (Transpiled with SystemJS)

        amdDependencyModule1.js (与SystemJS一起编译)

      • commonJSDependencyModule2.js (Transpiled with SystemJS)

        commonJSDependencyModule2.js (与SystemJS一起编译)

      • esCounterModule.js (Transpiled with SystemJS)

        esCounterModule.js (与SystemJS一起编译)

      • index.js (Transpiled with SystemJS)

        index.js (与SystemJS一起编译)

      lib

      LIB

      • amdDependencyModule1.js

        amdDependencyModule1.js

      • commonJSDependencyModule2.js

        commonJSDependencyModule2.js

      • esCounterModule.js

        esCounterModule.js

      • index.js

        index.js

      src

      src

    • babel.config.json

      babel.config.json

    root

Now all the ADM, CommonJS, and ES module syntax are transpiled to SystemJS syntax:

现在,所有ADM,CommonJS和ES模块语法都被转换为SystemJS语法:

// Transpile AMD/RequireJS module definition to SystemJS syntax: lib/amdDependencyModule1.js.
System.register([], function (_export, _context) {
    "use strict";
    return {
        setters: [],
        execute: function () {
            // Define AMD module: src/amdDependencyModule1.js
            define("amdDependencyModule1", () => {
                const api1 = () => { };

                return {
                    api1
                };
            });
        }
    };
});

// Transpile CommonJS/Node.js module definition to 
// SystemJS syntax: lib/commonJSDependencyModule2.js.
System.register([], function (_export, _context) {
    "use strict";
    var dependencyModule1, api2;
    return {
        setters: [],
        execute: function () {
            // Define CommonJS module: src/commonJSDependencyModule2.js
            dependencyModule1 = require("./amdDependencyModule1");

            api2 = () => dependencyModule1.api1();

            exports.api2 = api2;
        }
    };
});

// Transpile ES module definition to SystemJS syntax: lib/esCounterModule.js.
System.register(["./amdDependencyModule1", "./commonJSDependencyModule2"], 
                function (_export, _context) {
    "use strict";
    var dependencyModule1, dependencyModule2, count, increase, reset;
    return {
        setters: [function (_amdDependencyModule) {
            dependencyModule1 = _amdDependencyModule.default;
        }, function (_commonJSDependencyModule) {
            dependencyModule2 = _commonJSDependencyModule.default;
        }],
        execute: function () {
            // Define ES module: src/esCounterModule.js.
            dependencyModule1.api1();
            dependencyModule2.api2();
            count = 0;

            increase = () => ++count;

            reset = () => {
                count = 0;
                console.log("Count is reset.");
            };

            _export("default", {
                increase,
                reset
            });
        }
    };
});

// Transpile ES module usage to SystemJS syntax: lib/index.js.
System.register(["./esCounterModule"], function (_export, _context) {
    "use strict";
    var esCounterModule;
    return {
        setters: [function (_esCounterModuleJs) {
            esCounterModule = _esCounterModuleJs.default;
        }],
        execute: function () {
            // Use ES module: src/index.js
            esCounterModule.increase();
            esCounterModule.reset();
        }
    };
});

TypeScript模块:转换为CJS,AMD,ES,系统模块 (TypeScript Module: Transpile to CJS, AMD, ES, System Modules)

TypeScript supports all JavaScript syntax, including the ES6 module syntax. When TypeScript transpiles, the ES module code can either be kept as ES6, or transpiled to other formats, including CommonJS/Node.js, AMD/RequireJS, UMD/UmdJS, or System/SystemJS, according to the specified transpiler options in tsconfig.json:

TypeScript支持所有JavaScript语法,包括ES6模块语法 。 当TypeScript转档时,根据tsconfig中指定的转档器选项,可以将ES模块代码保留为ES6,或转译为其他格式,包括CommonJS / Node.js,AMD / RequireJS,UMD / UmdJS或System / SystemJS。 json

{
    "compilerOptions": {
        "module": "ES2020", // None, CommonJS, AMD, System, UMD, ES6, ES2015, ES2020, ESNext.
    }
}

For example:

例如:

// TypeScript and ES module.
// With compilerOptions: { module: "ES6" }. 
// Transpile to ES module with the same import/export syntax.
import dependencyModule from "./dependencyModule";
dependencyModule.api();
let count = 0;
export const increase = function () { return ++count };


// With compilerOptions: { module: "CommonJS" }. Transpile to CommonJS/Node.js module:
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; };

// With compilerOptions: { module: "AMD" }. Transpile to AMD/RequireJS module:
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; };
});

// With compilerOptions: { module: "UMD" }. Transpile to UMD/UmdJS module:
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; };
});

// With compilerOptions: { module: "System" }. Transpile to System/SystemJS module:
System.register(["./dependencyModule"], function (exports_1, context_1) {
    "use strict";
    var dependencyModule_1, count, increase;
    var __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; });
        }
    };
});

The ES module syntax supported in TypeScript was called external modules.

TypeScript支持的ES模块语法称为外部模块。

内部模块和命名空间 (Internal Module and Namespace)

TypeScript also has a module keyword and a namespace keyword https://www.typescriptlang.org/docs/handbook/namespaces-and-modules.html#pitfalls-of-namespaces-and-modules. They were called internal modules:

TypeScript还具有一个module关键字和一个namespace关键字https://www.typescriptlang.org/docs/handbook/namespaces-and-modules.html#pitfalls-of-namespaces-and-modules 。 它们被称为内部模块:

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

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

They are both transpiled to JavaScript objects:

它们都被转换为JavaScript对象:

var Counter;
(function (Counter) {
    var count = 0;
    Counter.increase = function () { return ++count; };
    Counter.reset = function () {
        count = 0;
        console.log("Count is reset.");
    };
})(Counter || (Counter = {}));

TypeScript module and namespace can have multiple levels by supporting the . separator:

通过支持,TypeScript模块和名称空间可以具有多个级别. 分隔器:

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

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

The sub module and sub namespace are both transpiled to object’s property:

子模块和子名称空间都被转换为对象的属性:

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

TypeScript module and namespace can also be used in the export statement:

TypeScript模块和名称空间也可以在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;
    }
}

The transpilation is the same as submodule and 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 = {}));

结论 (Conclusion)

Welcome to JavaScript, which has so much drama - 10+ systems/formats just for modularization/namespace:

欢迎使用JavaScript,它有这么多戏剧性-仅用于模块化/命名空间的10多种系统/格式:

  1. IIFE module: JavaScript module pattern

    IIFE模块:JavaScript 模块模式

  2. Revealing module: JavaScript revealing module pattern

    显示模块 :JavaScript 显示模块模式

  3. CJS module: CommonJS module, or Node.js module

    CJS模块 :CommonJS模块或Node.js模块

  4. AMD module: Asynchronous Module Definition, or RequireJS module

    AMD模块 :异步模块定义或RequireJS模块

  5. UMD module: Universal Module Definition, or UmdJS module

    UMD模块 :通用模块定义或UmdJS模块

  6. ES module: ECMAScript 2015, or ES6 module

    ES模块 :ECMAScript 2015或ES6模块

  7. ES dynamic module: ECMAScript 2020, or ES11 dynamic module

    ES动态模块 :ECMAScript 2020或ES11动态模块

  8. System module: SystemJS module

    系统模块 :SystemJS模块

  9. Webpack module: transpile and bundle of CJS, AMD, ES modules

    Webpack模块 :CJS,AMD,ES模块的运输和捆绑

  10. Babel module: transpile ES module

    Babel模块 :可转换ES模块

  11. TypeScript module and namespace

    TypeScript模块和名称空间

Fortunately, now JavaScript has standard built-in language features for modules, and it is supported by Node.js and all the latest modern browsers. For the older environments, you can still code with the new ES module syntax, then use Webpack/Babel/SystemJS/TypeScript to transpile to older or compatible syntax.

幸运的是,现在JavaScript具有模块的标准内置语言功能,并且Node.js和所有最新的现代浏览器都支持它。 对于较旧的环境,您仍然可以使用新的ES模块语法进行编码,然后使用Webpack / Babel / SystemJS / TypeScript转换为较旧或兼容的语法。

历史 (History)

  • 16th April, 2020: Initial version

    2020年4月16 :初始版本

翻译自: https://www.codeproject.com/Articles/5265230/Understanding-all-JavaScript-Module-Formats-and-To

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值