amd规范下的define的参数以及⽤法
CLONE⽽来
1. AMD的由来
  前端技术虽然在不断发展之中,却⼀直没有质的飞跃。除了已有的各⼤著名框架,⽐如Dojo,JQuery,ExtJs等等,很多公司也都有着⾃⼰的前端开发框架。这些框架的使⽤效率以及开发质量在很⼤程度上都取决于开发者对其的熟悉程度,以及对JavaScript的熟悉程度,这也是为什么很多公司的技术带头⼈都喜欢开发⼀个⾃⼰的框架。开发⼀个⾃⼰会⽤的框架并不难,但开发⼀个⼤家都喜欢的框架却很难。从⼀个框架迁移到⼀个新的框架,开发者很有可能还会按照原有框架的思维去思考和解决问题。这其中的⼀个重要原因就是JavaScript本⾝的灵活性:框架没办法绝对的约束你的⾏为,⼀件事情总可以⽤多种途径去实现,所以我们只能在⽅法学上去引导正确的实施⽅法。庆幸的是,在这个层⾯上的软件⽅法学研究,⼀直有⼈在去不断的尝试和改进,CommonJS就是其中的⼀个重要组织。他们提出了许多新的JavaScript架构⽅案和标准,希望能为前端开发提供银弹,提供统⼀的指引。
  AMD规范就是其中⽐较著名⼀个,全称是Asynchronous Module Definition,即异步模块加载机制。从它的规范描述页⾯看,AMD很短也很简单,但它却完整描述了模块的定义,依赖关系,引⽤关系以及加载机制。从它被requireJS,NodeJs,Dojo,JQuery使⽤也可以看出它具有很⼤的价值,没错,JQuery
近期也采⽤了AMD规范。在这篇⽂章中,我们就将介绍AMD的性质,⽤法,优势以及应⽤场景。从AMD 中我们也能学习到如何在更⾼层⾯去设计⾃⼰的前端应⽤。
jquery框架定义2. AMD是什么
  作为⼀个规范,只需定义其语法API,⽽不关⼼其实现。AMD规范简单到只有⼀个API,即define函数:
  define([module-name?], [array-of-dependencies?], [module-factory-or-object]);
  其中:
  module-name: 模块标识,可以省略。
  array-of-dependencies: 所依赖的模块,可以省略。
  module-factory-or-object: 模块的实现,或者⼀个JavaScript对象。
  从中可以看到,第⼀个参数和第⼆个参数都是可以省略的,第三个参数则是模块的具体实现本⾝。后⾯将介绍在不同的应⽤场景下,他们会使⽤不同的参数组合。
  从这个define函数AMD中的A:Asynchronous,我们也不难想到define函数具有的另外⼀个性质,异步性。当define函数执⾏时,它⾸先会异步的去调⽤第⼆个参数中列出的依赖模块,当所有的模块被载⼊完成之后,如果第三个参数是⼀个回调函数则执⾏,然后告诉系统模块可⽤,也就通知了依赖于⾃⼰的模块⾃⼰已经可⽤。如果对应到dojo1.6之前的实现,那么在功能上可以有如下对应关系:
  module-name: dojo.provide
  dependencies: quire
  module-factory: dojo.declare
  不同的是,在加载依赖项时,AMD⽤的是异步,⽽quire是同步。异步和同步的区别显⽽易见,前者不会阻塞浏览器,有更好的性能和灵活性。⽽对于NodeJs这样的服务器端AMD,则模块载⼊⽆需阻塞服务器进程,同样提⾼了性能。
3. 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();
    }
  });
4. 匿名模块
  define ⽅法允许你省略第⼀个参数,这样就定义了⼀个匿名模块,这时候模块⽂件的⽂件名就是模块标识。如果这个模块⽂件放在a.js 中,那么a就是模块名。可以在依赖项中⽤"a"来依赖于这个匿名模块。这带来⼀个好处,就是模块是⾼度可重⽤的。你拿来⼀个匿名模块,随便放在⼀个位置就可以使⽤它,模块名就是它的⽂件路径。这也很好的符合了DRY(Don't Repeat Yourself)原则。
  下⾯的代码就定义了⼀个依赖于alpha模块的匿名模块:
  define(["alpha"], function (alpha) {
    return {
      verb: function(){
      return alpha.verb() + 2;
      }
    };
  });
5. 仅有⼀个参数的define
  前⾯提到,define的前两个参数都是可以省略的。第三个参数有两种情况,⼀种是⼀个JavaScript对象,另⼀种是⼀个函数。
  如果是⼀个对象,那么它可能是⼀个包含⽅法具有功能的⼀个对象;也有可能是仅提供数据。后者和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到本地项⽬,都节约了开发时间和维护时间。
  如果参数是⼀个函数,其⽤途之⼀是快速开发实现。适⽤于较⼩型的应⽤,你⽆需提前关注⾃⼰需要什么模块,⾃⼰给谁⽤。在函数中,可以随时require⾃⼰需要的模块。例如:
    define(function(){
      var p = require('china');
      //使⽤china这个模块
    });
即你省略了模块名,以及⾃⼰需要依赖的模块。这不意味着你⽆需依赖于其他模块,⽽是可以让你在需要的时候去require这些模块。define ⽅法在执⾏的时候,会调⽤函数的toString⽅法,并扫描其中的require调⽤,提前帮助你载⼊这些模块,载⼊完成之后再执⾏。这使得快速开发成为可能。需要注意的⼀点是,Opera不能很好的⽀持函数的toString⽅法,因此,在浏览器中它的适⽤性并不是很强。但如果你是通过build⼯具打包所有的 JavaScript⽂件,这将不是问题,构建⼯具会帮助你扫描require并强制载⼊依赖的模块。
6. Dojo中的AMD
  Dojo 的1.6版本,其中⼀个重要的变化就是引⼊了AMD机制,取代了原来的dojo.provide和quire⽅法。但是现在仍然保持了向后兼容性,你仍然可以⽤dojo.provide和quire来定义和加载模块。需要注意的是:在 Dojo 1.6 中,针对 AMD 的重构仍然属于⼀个过渡期的改动 , ⽤户⾃⼰开发的 AMD 模块还不能被 Dojo 的加载器和 Build 系统⽀持 . 1.6 中现有的编译系统对AMD的⽀持还⾮常局限。如果你⾃⼰开发了 AMD 格式的模块,并且你仍然在使⽤默认的 Dojo 同步模块加载器,那么你必须严格遵循 Dojo 模块的格式 ( 包括换⾏的格式 )来保证你⾃⼰的模块能够成功编译。总结起来有以下三点:
  ⽤传统的⽅法 (quire()/dojo.provide()) – 这些模块,只能被 Dojo 同步加载器加载,但可以被 Dojo 编译系统(Build System )正确的编译
  ⽤ Dojo 同步加载器来加载 AMD 格式( define ())模块 – 这些模块可以被正常的加载,并且可以被其他兼容 AMD 格式的加载器加载 .现在虽然 Dojo1.6 还没有正式⽀持这种⽤法,但在⽬前的 Dojo1.6 编译系统中,是可以正常⼯作的 . ( 前提是你必须严格遵循 Dojo 模块定义的代码规范 )
  使⽤第三⽅加载器来加载 AMD 格式( define ())模块 – 模块可以被正常加载,并且可以被其他加载器所使⽤ . 这些模块可以使⽤RequireJS 或 Backdraft 提供的编译系统正常编译,但是 Dojo 还没有正式的测试过和其他加载器的兼容性 .
 以Calendar为例,⽤define⽅法来定义这个模块:
 define("dijit/Calendar",
    ["dojo", "dijit", "text!dijit/templates/Calendar.html",
    "dojo/cldr/supplemental", "dojo/date", "dojo/date/locale",
    "dijit/_Widget", "dijit/_Templated", "dijit/_CssStateMixin", "dijit/form/DropDownButton"],
    function(dojo, dijit) {
      dojo.declare(
        "dijit.Calendar",
        [dijit._Widget, dijit._Templated, dijit._CssStateMixin],
        {...}
     );
    return dijit.Calendar;
    }
  );
可以看到,模块标识就是模块⽂件的路径,模块本⾝⼀般都是dojo.declare定义的类。Dojo1.6中的dojo和dijit命名空间下的模块均已经⽤AMD的形式进⾏了重构,但dojox下仍然延⽤了传统的dojo.provide和quire形式。对AMD的引⼊是Dojo⾛向⾃动化包管理的重要⼀步,在后续⽂章中我们也将继续关注Dojo在这⽅⾯的进展。
7. 结论
  AMD 规范是JavaScript开发的⼀次重要尝试,它以简单⽽优雅的⽅式统⼀了JavaScript的模块定义和加载机制,并迅速得到很多框架的认可和采纳。这对开发⼈员来说是⼀个好消息,通过AMD我们降低了学习和使⽤各种框架的门槛,能够以⼀种统⼀的⽅式去定义和使⽤模块,提⾼开发效率,降低了应⽤维护成本。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。