• JavaScript模块化编程(四):AMD 与 CMD规范


    在JavaScript模块化编程的世界中,有两个规范不得不提,它们分别是AMD和CMD。现在的JS库或框架,凡是模块化的,一般都是遵循了这两个规范其中之一。

    AMD(Asynchronous Module Definition)

    CommonJS
    在说AMD之前,先要提一下CommonJS。CommonJS是为了弥补JavaScript标准库过少的缺点而产生的,由于JS没有模块机制(ES6引入了模块系统,但浏览器全面支持估计还有好几年),CommonJS就帮助JS实现模块的功能。现在很热门的Node.js就是CommonJS规范的一个实现。

    CommonJS在模块中定义方法要借助一个全局变量exports,它用来生成当前模块的API:

    /* math module */

    exports.add = function(a, b) {
    return a + b;
    };
    要加载模块就要使用CommonJS的一个全局方法require()。加载之前实现的math模块像这样:

    var math = require(‘math’);
    加载后math变量就是这个模块对象的一个引用,要调用模块中的方法就像调用普通对象的方法一样了:

    var math = require(‘math’);
    math.add(1, 3);
    总之,CommonJS就是一个模块加载器,可以方便地对JavaScript代码进行模块化管理。但它也有缺点,它在设计之初并没有完全为浏览器环境考虑,浏览器环境的特点是所有的资源,不考虑本地缓存的因素,都需要从服务器端加载,加载的速度取决于网络速度,而CommonJS的模块加载过程是同步阻塞的。也就是说如果math模块体积很大,网速又不好的时候,整个程序便会停止,等待模块加载完成。

    随着浏览器端JS资源的体积越来越庞大,阻塞给体验带来的不良影响也越来越严重,终于从,在CommonJS社区中有了不同的声音,AMD规范诞生了。

    AMD
    它的特点便是异步加载,模块的加载不会影响其他代码的运行。所有依赖于某个模块的代码全部移到模块加载语句的回调函数中去。AMD的require()语句接受两个参数:

    // require([module], callback)
    require([‘math’], function(math) {
    math.add(1, 3);
    });
    在回调函数中,可以通过math变量引用模块。

    AMD规范也规定了模块的定义规则,使用define()函数。

    define(id?, dependencies?, factory);
    它接受三个参数:
    id
    这是一个可选参数,相当于模块的名字,加载器可通过id名加载对应的模块。如果没有提供id,加载器会将模块文件名作为默认id。

    dependencies
    可选,接受一个数组参数,传入当前对象依赖的对象id。

    factory
    回调函数,在依赖模块加载完成后会调用,它的参数是所有依赖模块的引用。回调函数的返回值就是当前对象的导出值。

    用AMD规范实现一个简单的模块可以这样:

    define(‘foo’, [‘math’], function(math) {
    return {
    increase: function(x) {
    return math.add(x, 1);
    }
    };
    });
    如果省去id和dependencies参数的话,就是一个完全的匿名模块。factory的参数将为默认值require,exports和module加载器将完全通过文件路径的方式加载模块,同时如果有依赖模块的话可通过require方法加载。

    define(function(require, exports, module) {
    var math = require(‘math’);

    exports.increase = function(x) {
    return math(x, 1);
    };
    });
    AMD规范也允许对加载进行一些配置,配置选项不是必须的,但灵活更改配置,会给开发带来一些方便。

    baseUrl 以字符串形式规定根目录的路径,以后在加载模块时都会以该路径为标准。在浏览器中,工作目录的路径就是运行脚本的网页所在的路径。

    {
    baseUrl: ‘./foo/bar’
    }
    path 可以指定需加载模块的路径,模块名与路径以键-值对的方式写在对象中。如果一个模块有多个可选地址,可以将这些地址写在一个数组中。

    {
    path: {
    ‘foo’: ‘./bar’
    }
    }
    关于模块路径的设置项还有packages,map。

    shim
    对于某些没有按照AMD规范编写的模块,比如jQuery,来说,要使它们能被加载器加载,需要用shim方法为其配置一些属性。在main模块中,用require.config()方法:

    require.config({
    shim: {
    ‘jquery’: {
    exports: ‘$’
    },
    ‘foo’: {
    deps: [
    ‘bar’,
    ‘jquery’
    ],
    exports: ‘foo’
    }
    }
    });
    之后再用加载器加载就可以了。

    目前实现了AMD规范的库有很多,比较有名的是Require.js。

    CMD(Common Module Definition)

    CMD在很多地方和AMD有相似之处,在这里我只说两者的不同点。

    首先,CMD规范和CommonJS规范是兼容的,相比AMD,它简单很多。遵循CMD规范的模块,可以在Node.js中运行。

    define
    与AMD规范不同的是CMD规范中不使用id和deps参数,只保留factory。其中:
    1.factory接收对象/字符串时,表明模块的接口就是对象/字符串。

    define({ ‘foo’: ‘bar’ });

    define(‘My name is classicemi.’);
    define.cmd
    其值为一个空对象,用于判断页面中是否有CMD模块加载器。

    if (typeof define === ‘function’ && define.cmd) {
    // 使用CMD模块加载器编写代码
    }
    require
    此函数同样用于获取模块接口。如需异步加载模块,使用require.async方法。

    define(function(require, exports, module) {
    require.async(‘math’, function(math) {
    math.add(1, 2);
    });
    });
    我们可以发现,require(id)的写法和CommonJS一样是以同步方式加载模块。要像AMD规范一样异步加载模块则使用define.async方法。

    exports
    此方法用于模块对外提供接口。

    define(function(require, exports, module) {
    // 对外提供foo属性
    exports.foo = ‘bar’;

    // 对外提供add方法
    exports.add = function(a, b) {
    return a + b;
    }
    });
    提供接口的另一个方法是直接return包含接口键值对的对象:

    define(function(require, exports, module) {
    return {
    foo: ‘bar’,
    add: function(a, b) {
    return a + b;
    }
    }
    });
    但是注意,不能用exports输出接口对象:

    define(function(require, exports, module) {
    exports = {
    foo: ‘bar’,
    add: function(a, b) {
    return a + b;
    }
    }
    });
    这样写是错误的!
    替代方式是这样写:

    define(function(require, exports, module) {
    module.exports = {
    foo: ‘bar’,
    add: function(a, b) {
    return a + b;
    }
    }
    });
    之前错误的原因是在factory内部,exports实际上是module.exports的一个引用,直接给exports赋值是不会改变module.exports的值的。

    在module对象上,除了有上面提到的exports以外,还有一些别的属性和方法。
    module.id
    模块的标识。

    define(‘math’, [], function(require, exports, module) {
    // module.id 的值为 math
    });
    module.uri
    模块的绝对路径,由模块系统解析得到。

    define(function(require, exports, module) {
    console.log(module.uri); // http://xxx.com/path/
    });
    module.dependencies
    值为一个数组,返回本模块的依赖。

  • 相关阅读:
    js模版引擎handlebars.js实用教程——由于if功力不足引出的Helper
    js模版引擎handlebars.js实用教程——if-判断的基本用法
    js模版引擎handlebars.js实用教程——with-终极this应用
    js模版引擎handlebars.js实用教程——with-进入到某个属性(进入到某个上下文环境)
    js模版引擎handlebars.js实用教程——each-循环中使用this
    mysql 索引及查询优化总结
    面试篇——mysql
    设计模式六大原则(5):迪米特法则
    BigInteger与BigDecimal
    Java基本类型占用字节数(或 bit数)
  • 原文地址:https://www.cnblogs.com/clongxiang/p/4321568.html
Copyright © 2020-2023  润新知