AMD规范

在读某第三方的源代码时,发现一个define()方法,随即开始了AMD语法的学习,随将学习成果分享如下:

一,ADM规范背景调查

前端技术在不断的发展之中,很多公司会开发自己的框架来用。开发一个自己会用的框架并不难,但开发一个大家都喜欢的框架却很难。从一个框架迁移到一个新的框架,开发者很有可能还会按照原有框架的思维去思考和解决问题。这其中的一个重要原因就是JavaScript本身的灵活性:框架没办法绝对的约束你的行为,一件事情总可以用多种途径去实现,所以我们只能在方法学上去引导正确的实施方法。

庆幸的是,在这个层面上的软件方法学研究,一直有人在去不断的尝试和改进,CommonJS就是其中的一个重要组织。他们提出了许多新的JavaScript架构方案和标准,希望能为前端开发提供引导,提供统一的指引。

AMD规范也其中比较著名一个。AMD规范,全称是Asynchronous Module Definition,即异步模块加载机制。从它的规范描述页面看,AMD很短也很简单,但它却完整描述了模块的定义,依赖关系,引用关系以及加载机制。从它被requireJS,NodeJs,Dojo,JQuery使用也可以看出它具有很大的价值,没错,JQuery近期也采用了AMD规范。

AMD规范是RequireJS在推广过程中对模块定义的规范化产出。

AMD规范主要用于浏览器,由于该规范不是原生JS支持的,使用AMD规范进行开发时,需要引入第三方函数库,即RequireJS

RequireJS主要解决两个问题:

  1. 多个JS文件可能有依赖关系,被依赖的文件需要早于依赖它的文件加载到浏览器。
  2. JS加载的时候,浏览器会停止渲染页面,加载文件越多,浏览器失去响应时间越长。

来个简单的插曲 ---- RequireJS

1,RequireJS中定义模块 ---- 通过 define() 函数定义模块

define([module-name], [array-of-dependencies?], module-factory-or-object)

参数:

module-name: 模块标识,字符串类型,可选值。如果没有提供参数,默认为文件名。
  array-of-dependencies: 所依赖的模块,字符串数组,可选值
  module-factory-or-object: 模块的具体实现本身,初始化模块需要执行的函数或者一个JavaScript对象,必需。如果是函数,它只执行一次;如果是对象,此对象会作为模块的输出值。

// module1
define('module1', function() {
	// ...
	return {
		add: function() {
			return ...;
		}
		// ...
	}
});

// module2
define('module2',['module1'] function(module1) {
	// 使用module1模块中的方法
	module1.add();
	// ...
	return {
		// ...
	}
});

2,加载模块

模块的加载使用require()函数 ---- 异步加载模块,这样浏览器不会失去响应。

require([dependencies], function)

参数:

dependencies:字符串数组,该模块的依赖。

function:Function类型,所依赖的模块都加载成功之后回调。依赖的模块会以参数的形式传入该函数,从而在回调函数内部就可以使用这些模块。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>RequireJS</title>
    </head>
    <body>
        <div>RequireJS学习</div>
    </body>
    <script src="require.js"></script>
    <script type="text/javascript" src="module1.js"></script>
    <script type="text/javascript" src="module2.js"></script>
    <script type="text/javascript">
        require(['module1','module2'],function(module1, module2){
            // ...
        });
    </script>
</html>

好,插曲就到这里了,接下来言归正传。

二,ADM规范具体实现

AMD规范简单到只有一个API,即define函数:

define([module-name?], [array-of-dependencies?], module-factory-or-object);

参数:
  module-name: 模块标识,字符串类型,可选值。如果没有提供参数,默认为文件名。
  array-of-dependencies: 所依赖的模块,字符串数组,可选值AMD推崇依赖前置,即当前模块依赖的其他模块,模块依赖必须在真正执行具体的module-factory-or-object方法之前解决。
  module-factory-or-object: 模块的具体实现本身,初始化模块需要执行的函数或者一个JavaScript对象,必需。如果是函数,它只执行一次;如果是对象,此对象会作为模块的输出值。

当define函数执行时:它首先会异步的去调用第二个参数中列出的依赖模块,当所有的模块被载入完成之后,如果第三个参数是一个回调函数则执行,然后告诉系统模块可用,也就通知了依赖于自己的模块自己已经可用。

三,AMD示例:如何定义一个模块

下面代码定义了一个alpha模块,并且依赖于内置的require,exports模块,以及外部的beta模块。可以看到,第三个参数是回调函数,可以直接使用依赖的模块,他们按依赖声明顺序作为参数提供给回调函数。

这里的require函数让你能够随时去依赖一个模块,即取得模块的引用,从而即使模块没有作为参数定义,也能够被使用;exports是定义的alpha 模块的实体,在其上定义的任何属性和方法也就是alpha模块的属性和方法。通过exports.verb = …就是为alpha模块定义了一个verb方法。例子中是简单调用了模块beta的verb方法。

define(“alpha”, [“require”, “exports”, “beta”], function (require, exports, beta) {

    exports.verb = function() {

        return beta.verb();

        //或者:

        return require(“beta”).verb();

    }

});

四,匿名模块

define 方法允许你省略第一个参数,这样就定义了一个匿名模块,这时候模块文件的文件名就是模块标识。如果这个模块文件放在a.js中,那么a就是模块名。可以在依赖项中用”a”来依赖于这个匿名模块。这带来一个好处,就是模块是高度可重用的。你拿来一个匿名模块,随便放在一个位置就可以使用它,模块名就是它的文件路径。这也很好的符合了DRY(Don’t Repeat Yourself)原则。

下面的代码就定义了一个依赖于alpha模块的匿名模块:

define([“alpha”], function (alpha) {
    return {
        verb: function(){
            return alpha.verb() + 2;
        }
    };
});

五,仅有一个参数的define

前面提到,define的前两个参数都是可以省略的。

第三个参数有两种情况,一种是一个JavaScript对象,另一种是一个函数。

1,第三个参数为对象

如果是一个对象,那么它可能是一个包含方法具有功能的一个对象;也有可能是仅提供数据。后者和JSON-P非常类似,因此AMD也可以认为包含了一个完整的 JSON-P实现。模块演变为一个简单的数据对象,这样的数据对象是高度可用的,而且因为是静态对象,它也是CDN友好的,可以提高JSON-P的性能。考虑一个提供中国省市对应关系的JavaScript对象,如果以传统JSON-P的形式提供给客户端,它必须提供一个callback函数名,根据这个函数名动态生成返回数据,这使得标准JSON-P数据一定不是CDN友好的。但如果用AMD,这个数据文件就是如下的形式:

define({
    provinces: [
        {
            name: ‘上海’,
            areas: [‘浦东新区’, ‘徐汇区’]
        },{
            name: ‘江苏’,
            cities: [‘南京’, ‘南通’]
        },
        // ......
    ]
});

假设这个文件名为china.js,那么如果某个模块需要这个数据,只需要:

define([‘china’, function(china){
    //在这里使用中国省市数据
});

通过这种方式,这个模块是真正高度可复用的,无论是用远程的,还是Copy到本地项目,都节约了开发时间和维护时间。

2,第三个参数为函数

如果参数是一个函数,其用途之一是快速开发实现。适用于较小型的应用,你无需提前关注自己需要什么模块,自己给谁用。在函数中,可以随时require自己需要的模块。例如:

define(function(){
    var p = require(‘china’);
    //使用china这个模块
});

即你省略了模块名,以及自己需要依赖的模块。这不意味着你无需依赖于其他模块,而是可以让你在需要的时候去require这些模块。define方法在执行的时候,会调用函数的toString方法,并扫描其中的require调用,提前帮助你载入这些模块,载入完成之后再执行。这使得快速开发成为可能。需要注意的一点是,Opera浏览器不能很好的支持函数的toString方法,因此,在浏览器中它的适用性并不是很强。但如果你是通过build工具打包所有的 JavaScript文件,这将不是问题,构建工具会帮助你扫描require并强制载入依赖的模块。

六,结论

AMD 规范是JavaScript开发的一次重要尝试,它以简单而优雅的方式统一了JavaScript的模块定义和加载机制,并迅速得到很多框架的认可和采纳。这对开发人员来说是一个好消息,通过AMD我们降低了学习和使用各种框架的门槛,能够以一种统一的方式去定义和使用模块,提高开发效率,降低了应用维护成本。

七,下集预告

CMD规范

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值