AMD、CMD、CommonJS 和 ES6 模块化规范

演变历史

    早期(全局函数模式)
    Global 被污染,很容易命名冲突

function foo(){}
function bar(){}

    1
    2

    中期(命名空间模式)
    本质是对象,可以被操作修改,不安全

var DO = {
    foo: function () { },
    bar: function () { }
}
DO.foo()

    1
    2
    3
    4
    5

    后期(IIFE模式)
    立即执行函数

(function ($) {
    function foo() {
        console.log($); // window 对象
    }
    function bar() { }
    $.module = { foo: foo, bar: bar }
})(window)
module.foo()

    1
    2
    3
    4
    5
    6
    7
    8

问题:上面的几种方式尽管可以模块化,但是请求过多、依赖模糊、难以维护,还是达不到想要的目的,所以产生了四种模块化规范(AMD、CMD、CommonJS、ES6)

问题举例:

// js/module.js
(function ($) {
    let fun = 'i am module'
    function out() {
        return fun
    }
    $.module = { out }
})(window)

// js/app.js
(function ($) {
    let fun = 'i am app'
    function out() {
        console.log(fun, '————', module.out())
    }
    $.app = { out }
})(window, module)

// index.html
<script src="./js//module.js"></script>
<script src="./js/app.js"></script>
<script>
    app.out() // i am app ———— i am module
</script>

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24

模块化目的

    降低复杂度
    提升解耦性
    避免命名冲突
    部署方便,复用性强,按需加载
    可维护性好

AMD

    RequireJS 在推广过程中对模块定义的规范化产出
    依赖前置,异步模块定义,在使用前需要先定义
    专门用于浏览器端,模块的加载是异步的

基本语法:官方文档

    暴露模块

// 定义有依赖的模块
// 数组内放置依赖的模块,回调内形参对应前面依赖的模块
define(['module1','module2'], function(m1, m2) {
    'use strict';
    // return 模块
});

// 定义没有依赖的模块
define(function() {
    'use strict';
    // return 模块
});

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    引入模块

require(['module1','module2'], function(m1, m2) {
    // 使用 m1、m2
})

    1
    2
    3

实例:

// js/libs/module1.js
// 定义有依赖的模块
define(['module2'], function (module2) {
    let msg = 'module1'
    function showMsg() {
        console.log(msg, '————', module2.out());
    }
    // 暴露模块
    return { showMsg }
});

// js/libs/module2.js
// 定义没有依赖的模块
define(function () {
    let msg = 'module2'
    function out() {
        return msg
    }
    // 暴露模块
    return { out }
});

// js/main.js
(function () {
    requirejs.config({ // 配置模块
        //By default load any module IDs from js/lib
        baseUrl: 'js/', // 基本路径,出发点在根路径下

        //except, if the module ID starts with "app",
        //load it from the js/app directory. paths
        //config is relative to the baseUrl, and
        //never includes a ".js" extension since
        //the paths config could be for a directory.
        paths: { // 配置路径,注意尾部不要带 .js
            // app: '../app'
            module1: './libs/module1',
            module2: './libs/module2',
        }
    })

    requirejs(['module1'], function (module1) {
        module1.showMsg()
    })
})();

// app.html
<script data-main='js/main' src='js/libs/require.js'></script>
// data-main 用于指向主文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48

CMD

    SeaJS 在推广过程中对模块定义的规范化产出
    依赖就近,同步模块定义,即用即返回
    专门用于浏览器端,模块的加载是异步的

基本语法:官方文档

    暴露模块

// 定义有依赖的模块
define(function (require, exports, module) {
    // 引入依赖模块(同步)
    var module2 = require('./module2')
    // 引入依赖模块(异步)
    require.async('./module3', function (m3) {
        // m3 模块引入成功后在回调内直接使用
    })
    // 暴露模块
    exports.xxx = value
})

// 定义没有依赖的模块
define(function (require, exports, module) {
    // 暴露模块
    exports.xxx = value
    module.exports = value
})

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18

    引入模块

define(function (require) {
    var m1 = require('./module1')
    var m4 = require('./module4')
    m1.show()
    m4.show()
})

    1
    2
    3
    4
    5
    6

实例:

// js/libs/module1.js
// 定义有依赖的模块
define(function (require, exports, module) {
    // 异步引入
    require.async('./module2', function (m2) {
        console.log(m2.out());
    })
    
    // 同步引入
    let module2 = require('./module2')
    let msg = 'module1'
    function showMsg() {
        console.log(msg, '————', module2.out());
    }

    // 暴露模块
    module.exports = showMsg
});

// js/libs/module2.js
// 定义没有依赖的模块
define(function (require, exports, module) {
    let msg = 'module2'
    function out() {
        return msg
    }
    module.exports = { out }
});

// js/main.js
define(function (require) {
    let module1 = require('./libs/module1')
    module1() // module2 / module1 ———— module2
})

// app.html
<script src='js/libs/sea.js'></script>
<script>
    seajs.use('./js/main.js')
</script>

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40

CommonJS

    每个文件都可当做一个模块
    在服务器端:模块的加载是运行时同步加载的
    在浏览器端:模块需要提前编译(Browserify)打包处理(在浏览器端推荐 ES6 模块化,因为同步等待时间长,性能差)

基本语法:更多

// 模块暴露
// 本质暴露的是 exports 对象(初始 exports 为 {})
module.exports = value
exports.xxx = value
console.log(exports === module.exports) // true

// 引入模块
require(xxx) // 第三方模块直接引入,自定义模块按路径引入

    1
    2
    3
    4
    5
    6
    7
    8

ES6

基本语法:更多

// 暴露模块
export default value
export let a = { name: 'foo' }
export let b = { name: 'bar' }

// 引入模块
import value from '../path1'
import { a, b } from './path2'

    1
    2
    3
    4
    5
    6
    7
    8

AMD 与 CMD 差异

    AMD 是 RequireJS 在推广过程中对模块定义提出的概念。
    CMD 是 SeaJS 在推广过程中对模块定义提出的概念。

RequireJS 和 Sea.js 都是模块加载器,倡导模块化开发理念,核心价值是让 JavaScript 的模块化开发变得简单自然。
不同之处,两者的主要区别如下:

    定位有差异。RequireJS 想成为浏览器端的模块加载器,同时也想成为 Rhino / Node 等环境的模块加载器。Sea.js 则专注于 Web 浏览器端,同时通过 Node 扩展的方式可以很方便跑在 Node 环境中。
    遵循的规范不同。RequireJS 遵循 AMD(异步模块定义)规范,Sea.js 遵循 CMD (通用模块定义)规范。规范的不同,导致了两者 API 不同。Sea.js 更贴近 CommonJS Modules/1.1 和 Node Modules 规范。
    CMD 推崇依赖就近,AMD 推崇依赖前置。
    推广理念有差异。RequireJS 在尝试让第三方类库修改自身来支持 RequireJS,目前只有少数社区采纳。Sea.js 不强推,采用自主封装的方式来“海纳百川”,目前已有较成熟的封装策略。
    对开发调试的支持有差异。Sea.js 非常关注代码的开发调试,有 nocache、debug 等用于调试的插件。RequireJS 无这方面的明显支持。
    插件机制不同。RequireJS 采取的是在源码中预留接口的形式,插件类型比较单一。Sea.js 采取的是通用事件机制,插件类型更丰富。
————————————————
版权声明:本文为CSDN博主「肖ZE」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lucky541788/java/article/details/88967378

原文地址:https://www.cnblogs.com/fs0196/p/13048371.html