介绍 Dojo中的AMD 模块 <1>

Dojo 支持AMD规范的模块, 使得代码更容易编写,规范及调试。 在这个教程中, 我们会AMD的基础知识及使用.


概述

Dojo 从 1.7版本开始采用异步模块定义(AMD) 规范, 对比于传统的Dojo 模块, AMD提供了更多的增强功能, 包括完全异步操作,真正的包可移值,更好的依赖管理,以及改进的调试支持。 AMD也是一社区发展起来的 标准,意味着采用AMD规范书写的模块不仅可以用Dojo的loader加载, 也适合于其它兼容于AMD规范的加载器,如requirejs. 在这个教程中,我们将解释 AMD 及 教你如何使用。

什么是模块?

模块是可以被单次引用的一个值,如果你有多段数据或者函数,你可以在一个模块中爆露这次数据或函数,它们可以是单个对象的属性, 这个对象就刚好代表一个模块。实际上,可以夸张到创建一个var tinyModule='simple value' 这样一个模块,虽然简单,但这是有效的。 模块组就是代码被分害成逻辑子集来处理特定的功能,模块化的代码会变得更有意义。如果你想通过名字,地址等信息来表现示一个人,或者给这个人添加一些方法。它开始把有意义的所有代码放到单个位置,一个模块存储在文件系统的单个文件里。

如何创建一个模块?

在AMD下,你创建一个模块需要通过loader来注册它。

loader? 什么是loader? loader就是处理定义模块和加载模块逻辑背后的代码片段。 当你加了了dojo.js或者require.js,你就获得了一个AMD Loader. Loader爆露两个函数 reuqire and define. 通过这两个函数你可以与loader交互。

全局函数define 允许你通过loader去注册一个模块, 让我们看看如何创建一个模块。

defined(5)
不是很复杂,却是有效的,仅仅定义一个模块,它的值为5.
define({
    library: 'dojo',version: 1.9
});
变得有趣一点,当这个模块被加载了, 我们将获得一个拥有两个属性的对象。
define(function(){
    var privateValue = 0;
    return {
        increment: function(){
            privateValue++;
        },decrement: function(){
            privateValue--;
        },getValue: function(){
            return privateValue;
        }
    };
});
在这个例子中,我们将一个函数传给给define. 这个函数会被运行求值,将结果是一个对象,会被loader作为一个模块来存储。 这里使用一个闭包的方法创建一个私有变量,私有变量不会被外部引用到,但是可以被返回的对象访问及操作。

如何加载一个模块?

首先, 我们需要知道模块如何被识别。 为了加载一个模块,你需要一些方法去标识模块。 与其它语言的模块/包系统相同, 一个AMD模块被标识为它的路径和文件名称。 让我将上面的例子保存在一个文件夹里。
app/counter.js
接着让我们添加一个loader(这里当然用dojo了)及一个主文件 index.html(应用程序的入口)。 下面展示了整个的结构
/
	index.html
	/dojo/
	/app/
		counter.js

index.html 内的代码如下
<html>
    <body>
        <script src="dojo/dojo.js" data-dojo-config="async: true"></script>
        <script>
            require([
                "app/counter"
            ],function(counter){
                log(counter.getValue());
                counter.increment();
                log(counter.getValue());
                counter.decrement();
                log(counter.getValue());
            });
        </script>
    </body>
</html>

到目前为此,让我们回顾下:

1. 在 app/counter.js,我们通过调用loader 的 define 函数来注册一个模块, 要注意的是我们定义的这个模块是引用一个对象,而不是这个匿名构造函数。 这个意识是每一个加载这个模块的代码将会获得完全一样的一个对象, 一般的,模块返回一个构造函数,但是在许多情况下,更适合于返回单个对象。

2. 在包含index.html的文件夹下的子目录中,以及在AMD Loader(dojo/dojo.js)目录中,我们都没有做额外的配置,加载器并不知道模块在哪个位置,所以模块的标识符"app/counter" 表明 loader 应该加载 app/counter.js 并用使用它返回的值来做为一个模块。

3. 在我们的index.html,我们通过 require来加载 "app/counter"模块。 你可以简单的使用requre(['app/counter']) 来加载一个模块。 如果你需要引用加载的模块, 你需要将应用一个回调函数。 加载器会确保模块被加载,一但被加载了, 它将调用你的回调函数,并将加载的一些模块作为参数传递给回调函数。 正如其它的函数一样, 你可以自由的定义参数的名称, 它没有要求参数的名字必须跟模块有对象的关系。 即例如此, 良好的习惯还是使用模块相似的名字。

模块加载模块

我们所有的例子都是非常简单的使用define函数。 当一个应用是由很多结构清晰的模块组成时, 自然在模块之前会有很多的依赖关系。 define 函数可以自动的加载它所依赖的模块。 在返回模块的值之前,依赖列表会传递给 define.

define([
    "dojo/dom","app/dateFormatter"
],function(dom,dateFormatter){
    return declare(null,{
        showDate: function(id,date){
            dom.byId(id).innerHTML = dateFormatter.format(date);
        }
    });
});

这个例子演示了AMD应用程序很多典行的特点:

1. 多种依赖 - "dojo/dom" 与 “app/dataFormatter”(假设的) 模块都被指定在依赖列表里。
2. 返回一个构造函数 - 对于这个模块会有一个土适应的名称,比如" app/DateManager". 代码举例:
require([
    "app/DateManager"
],function(DateManager){
    var dm = new DateManager();
    dm.showDate('dateElementId',new Date());
});

* 虽然在开发Dojo之前AMD是你需要了解的主题之一, decalre 是别外重要的一个函数。 如果你还没有熟悉 dojo/_base/decalre,可以阅读下它的 教程

使用插件

除了常规的模块以外, AMD loader 还可以使用插件。 插件被用来扩展loader的新功能,而不是简单的加载一个AMD模块, 插件被加载或多或少的与常规模块是差不多的,只是需要在模块标识符的未尾添加一个 "!"来标识这个请求是一个插件请求。 请求的数据要放在"!"之后, 会被 直接传递给插件去处理。 我们通过一些例子,将会非常清晰的了解赖个过程 。 Dojo 在默认的提供数个插件。 最重要的5个是 dojo/text,dojo/i18n,dojo/has 以及 dojo/domReady. 让我们看看如何来使用它们。

dojo/text

当你需要从一个文件中(如html 模板)加载字符串时, 可以使用dojo/text. 返回的值可以被缓存, 之后的请求调用同一个文件时将不会增加额外的网络请求。 builder( dojo的一个工具)的字符加载就会内联dojo/text. 举一个例子,为了给窗口部件(widget)加载一个模板, 你要像以下这样定义你的模板:

// in "my/widget/NavBar.js"
define([
    "dojo/_base/declare","dijit/_WidgetBase","dijit/_TemplatedMixin","dojo/text!./templates/NavBar.html"
],function(declare,_WidgetBase,_TemplatedMixin,template){
    return declare([_WidgetBase,_TemplatedMixin],{
        // template contains the content of the file "my/widget/templates/NavBar.html"
        templateString: template
    });
});

dojo/i18n

dojo/i18n 会依据浏览器的user locale 来加载语言包。 使用如下:
// in "my/widget/Dialog.js"
define([
    "dojo/_base/declare","dijit/Dialog","dojo/i18n!./nls/common"
],Dialog,i18n){
    return declare(Dialog,{
        title: i18n.dialogTitle
    });
});

如何使用i18n,查 更多国际化 指南

dojo/has

dojo loader 包含了一个实现has.js特征的API. dojo/has 插件利用这个功能有条件的请求加载模块。 使用如下

// in "my/events.js"
define([
    "dojo/dom","dojo/has!dom-addeventlistener?./events/w3c:./events/ie"
],events){
    // events is "my/events/w3c" if the "dom-addeventlistener" test was true,"my/events/ie" otherwise
    events.addEvent(dom.byId("foo"),"click",function(){
        console.log("Foo clicked!");
    });
});

dojo/domReady

dojo/domReady 是替换原来的dom.ready. 在dom 状态不是ready时, 模块不会被解析。 使用如下
// in "my/app.js"
define(["dojo/dom","dojo/domReady!"],function(dom){
    // This function does not execute until the DOM is ready
    dom.byId("someElement");
});

* 注意我们没有在回调函数中定一个dojo/domReady的返回值参数。 这是因为它的返回值是没有意义的 - 我们只是简单的使用它去延迟回调函数的调用。 请求的模块或者插件不需要使用返回值时,需要将它们放在依赖列表的末尾, 因为模块与回调函数中的本地变量之间的顺序是对应的。

* 虽然没有数据要传递给插件, 感叹号也还是必需要的,如果没有, 你仅仅是加载了dojo/domReady 依赖模块代替了它激活的插件功能。

总结

本教程中你了解AMD基础后你可以开始开发 dojo了。 但你不久就会陷入复杂的情形, 可以参考更多高级的 AMD使用教程:
  • 配置loader,以便在loader 及 其它包在不同的路径,甚至是不同的服务器都能工作。
  • 创建可移值的模块包
  • 加载同一个模块或者库的不同版本
  • 加载不是AMD 规范的代码

资源

相关文章

我有一个网格,可以根据更大的树结构编辑小块数据.为了更容易...
我即将开始开发一款教育性的视频游戏.我已经决定以一种我可以...
我正在使用带有Grails2.3.9的Dojo1.9.DojoNumberTextBox小部...
1.引言鉴于个人需求的转变,本系列将记录自学arcgisapiforja...
我正在阅读使用dojo’sdeclare进行类创建的语法.描述令人困惑...
我的团队由更多的java人员和JavaScript经验丰富组成.我知道这...