zoukankan      html  css  js  c++  java
  • 与 JavaScript 模块相关的所有知识点

    JavaScript 语言最初是为简单的表单操作而发明的,没有诸如模块或命名空间之类的内置功能。多年以来发明了大量的术语、模式、库、语法和工具来模块化 JavaScript。本文讨论了 JavaScript 中的所有主流模块系统、格式、库和工具,包括:

    • JavaScript 模块格式和工具大全

      • IIFE 模块:JavaScript 模块模式

        • IIFE:立即调用的函数表达式
        • 混合导入
      • Revealing 模块:JavaScript 显示模块模式
      • Cjs 模块:Commonjs 模块或 Node.js 模块
      • AMD 模块:异步模块定义或 RequireJS 模块

        • 动态加载
        • 来自 CommonJS 模块的 AMD 模块
      • UMD 模块:通用模块定义或 UmdJS 模块

        • 适用于AMD(RequireJS)和本机浏览器的 UMD
        • 适用于AMD(RequireJS)和CommonJS(Node.js)的UMD
      • ES 模块:ECMAScript 2015 或 ES6 模块
      • ES 动态模块:ECMAScript 2020 或 ES11 动态模块
      • 系统模块:SystemJS 模块

        • 动态模块加载
      • webpack 模块:来自 CJS、AMD、ES 模块的捆绑软件
      • Babel 模块:从 ES 模块转换

        • Babel with SystemJS
      • TypeScript 模块:转换为 CJS、AMD、ES、系统模块

        • 内部模块和命名空间
      • 结论

    希望本文可以帮助你了解和使用 JavaScript/TypeScript 语言,RequireJS/SystemJS 库和 webpack/Babel 工具等所有这些模式。

    IIFE 模块:JavaScript 模块模式

    在浏览器中,定义 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();

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

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

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

    IIFE:立即调用的函数表达式

    为了执行函数 f 中的代码,语法是将函数调用 () 作为 f()。为了在匿名函数 (() => {}) 中执行代码,可以将相同的函数调用语法 () 用作 (() => {}):

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

    这称为 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();

    它将模块代码包装在 IIFE 中,返回一个对象,这个对象是导出的 API 的占位符。仅引入 1 个全局变量,这是模式名称。之后模块名可用于调用导出的模块 API。这称为 JavaScript 的模块模式。

    混合导入

    定义模块时,可能需要一些依赖关系。使用 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);

    一些流行库(如 jQuery)的早期版本遵循这种模式。

    revealing module:JavaScript 揭示模块模式

    揭示模块模式由 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();

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

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

    CommonJS(最初名为 ServerJS)是定义和使用模块的模式。它由 Node.js 实现。默认情况下,每个 .js 文件都是 CommonJS 模块。为模块提供了暴露 API 的模块变量和导出变量。并且提供了一个 require 函数来使用模块。以下代码以 CommonJS 语法定义了 counter 模块:

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

    以下例子使用了 counter 模块:

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

    在运行时,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(Asynchronous Module Definition https://github.com/amdjs/amdj...)是一种定义和使用模块的模式。它由 RequireJS 库(https://requirejs.org/)实现。 AMD 提供了一个定义模块的定义函数,该函数接受模块名称、依赖模块的名称以及工厂函数:

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

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

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

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

    动态加载

    AMD 的 require 函数还有另一个重载。它接受一个回调函数,并将类似 CommonJS 的 require 函数传递给该回调。所以可以通过调用 require 来加载 AMD 模块:

    // 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 模块

    上面的 define 函数有一个重载,它可以传递 require 函数,并将变量和模块变量导出到回调中,以便 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(Universal Module Definition,https://github.com/umdjs/umd)是一组棘手的模式,可以使你的代码文件在多种环境中工作。

    适用于 AMD(RequireJS)和本机浏览器的 UMD

    例如以下是一种 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
        };
    });

    它比较复杂,但仍然只是 IIFE。匿名函数会检测是否存在 AMD 的 define 函数,如果存在,请使用 AMD 的define 函数调用模块工厂。如果不是,它将直接调用模块工厂。目前,root 参数实际上是浏览器的 window 对象。它从全局变量( window 对象的属性)获取依赖项模块。当 factory 返回模块时,返回的模块也被分配给一个全局变量( window 对象的属性)。

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

    以下是使模块定义与 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);

    别怕,这只是一个IIFE。调用IIFE时,将评估其参数。参数评估检测环境(CommonJS / Node.js的模块变量和exports 变量,以及 AMD/RequireJS 的 define 函数)。如果环境是 CommonJS/Node.js,则匿名函数的参数是手动创建的 define 函数。如果环境是 AMD/RequireJS,则匿名函数的参数就是 AMD 的 define 函数。因此,当执行匿名函数时,可以确保它具有有效的 define 函数。在匿名函数内部,它仅调用 define 函数来创建模块。

    ES 模块:ECMAScript 2015 或 ES6 模块

    在所有模块混乱之后,JavaScript 的规范第 6 版在 2015 年定义了完全不同的模块系统和语法。该规范称为ECMAScript 2015 或 ES2015,AKA 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
    };

    要在浏览器中使用此模块文件,请添加 <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();

    对于浏览器,可以将 <script> 的 nomodule 属性用于后备:

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

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

    在 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();
    });

    通过返回一个 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();
    
    })();

    以下是来自 https://developer.mozilla.org... 的导入、动态导入、导出的兼容性列表:

    系统模块:SystemJS 模块

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

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

    如果当前运行时(例如旧的浏览器)不支持 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
                });
            }
        };
    });

    这样新的 ES6 语法 import/export 就消失了。

    动态模块加载

    SystemJS 还提供了用于动态导入的 import 函数:

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

    Webpack 模块:来自 CJS,AMD,ES 模块的捆绑包

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

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

    以下文件使用了 counter 模块:

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

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

    • root

      • dist

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

        • amdDependencyModule1.js
        • commonJSDependencyModule2.js
        • esCounterModule.js
        • index.js
      • webpack.config.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'),
        },
    };

    现在运行以下命令以不同的语法转换和捆绑 4 个文件:

    npm install webpack webpack-cli --save-dev
    npx webpack --config webpack.config.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;
        }
    ]);

    同样,它只是一个 IIFE。所有 4 个文件的代码都转换为 4 个函数中的代码。并且这 4 个函数作为参数传递给匿名函数。

    Babel 模块:从 ES 模块转换

    Babel 是另一个为旧版环境(如旧版浏览器)把 ES6 + JavaScript 代码转换为旧版语法的编译器。可以将 ES6 import/export 语法中的上述 counter 模块转换为以下替换了新语法的 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
    };

    这是 index.js 中使用 counter 模块的代码:

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

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

    Babel 与 SystemJS

    SystemJS 可以用作 Babel 的插件:

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

    并将其添加到 Babel 配置中:

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

    现在 Babel 可以与 SystemJS 一起使用以转换 CommonJS/Node.js 模块、AMD/RequireJS 模块和 ES 模块:

    npx babel src --out-dir lib

    结果是:

    root

    • lib

      • amdDependencyModule1.js (与 SystemJS 一起编译)
      • commonJSDependencyModule2.js (与 SystemJS 一起编译)
      • esCounterModule.js (与 SystemJS 一起编译)
      • index.js (与 SystemJS 一起编译)
    • src

      • amdDependencyModule1.js
      • commonJSDependencyModule2.js
      • esCounterModule.js
      • index.js
    • babel.config.json

    现在所有 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 支持 ES 模块语法(https://www.typescriptlang.or...),根据 tsconfig.json 中指定的 transpiler 选项,可以将其保留为 ES6 或转换为其他格式,包括 CommonJS/Node.js、AMD/RequireJS、UMD/UmdJS 或 System/SystemJS:

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

    例如:

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

    这在 TypeScript 中称为外部模块。

    内部模块和命名空间

    TypeScript还具有一个 module 关键字和一个 namespace 关键字。它们被称为内部模块:

    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.");
        };
    }

    它们都被转换为 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 Counter.Sub {
        let count = 0;
        export const increase = () => ++count;
    }
    
    namespace Counter.Sub {
        let count = 0;
        export const increase = () => ++count;
    }

    它们被转换为对象的属性:

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

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

    编译后 sub 模块和 sub 命名空间相同:

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

    结论

    欢迎使用 JavaScript,它具有如此丰富的功能——仅用于模块化/命名空间的就有 10 多种系统和格式:

    1. IIFE module:JavaScript 模块模式
    2. 揭示模块:JavaScript 揭示模块模式
    3. CJS模块:CommonJS 模块或 Node.js 模块
    4. AMD 模块:异步模块定义或 RequireJS 模块
    5. UMD 模块:通用模块定义或 UmdJS 模块
    6. ES 模块:ECMAScript 2015 或 ES6 模块
    7. ES 动态模块:ECMAScript 2020 或 ES11 动态模块
    8. 系统模块:SystemJS 模块
    9. Webpack 模块:CJS、AMD、ES 模块的移植和捆绑
    10. Babel 模块:可移植 ES 模块
    11. TypeScript模块 和命名空间

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

    豌豆资源搜索网站https://55wd.com 广州vi设计公司http://www.maiqicn.com

  • 相关阅读:
    sql 保存,性能高
    老生常谈-从输入url到页面展示到底发生了什么
    HTTP 协议入门
    理解HTTP协议
    利用Vue v-model实现一个自定义的表单组件
    浅谈 Vue v-model指令的实现原理
    vuex介绍--一篇看懂vuejs的状态管理神器
    ES6之 =>箭头函数
    position定位-absolute与fixed
    JavaScript:理解事件、事件处理函数、钩子函数、回调函数
  • 原文地址:https://www.cnblogs.com/qianxiaox/p/13700345.html
Copyright © 2011-2022 走看看