首页 > 代码库 > AMD高级应用(翻译)

AMD高级应用(翻译)

Dojo now supports modules written in the Asynchronous Module Definition (AMD) format, which makes code easier to author and debug. In this tutorial, we learn all about this new module format, and explore how to write an application using it.

dojo现在支持异步模式定义的模块,这会让你的代码更有可读性以及可调试性。在本章中,我们会学习该模式,并了解如何在应用中使用它。

This tutorial is a follow-up to the Introduction to AMD, so make sure you understand the basics of AMD first.

本章节是Introduction to AMD,的延续,所以请你确定你已经理解了AMD的一些基础知识。

Throughout this tutorial, we will be referring to a hypothetical application with a filesystem structure that looks like this:

通过本文章,我们将会虚拟一个应用程序,其文件结构如下所示:

1 /2     index.html3     js/4         lib/5             dojo/6             dijit/7             dojox/8         my/9         util/

 As you can see, this structure is different from what we discussed in the previous tutorial, so we will explain how to configure the loader to make this work. But first let‘s revisit require and define with a few more details...

就像你看到的,本结构和我们上面那个例子中的文件结构不一样。所以我们会说明如何配置加载器,以保证代码可以运行。但是,在此之前,我们还是通过几个例子回顾一下require和define函数。

Delving Deeper into require

更深入的了解require函数

The require function accepts the following parameters:

require函数接受下面的参数。

  1. configuration (optional, default=undefined): an object with loader configuration options - this allows you to reconfigure the loader at run-time.

    配置:(可选的,默认值=undefined):加载器配置设置对象-该对象可以允许你在运行时重新配置加载器。

  2. dependencies (optional, default=[]): an array of module identifiers. If specified, these modules will be resolved before your code is evaluated. They will be loaded in the order they are listed and passed as parameters to your callback function, also in order.

    依赖(可选的,默认值=[]):模块标识数组。如果被指定,这些模块在你的代码被执行之前就会被加载。模块会按照其顺序加载,并作为参数按顺序传递给回调函数。

  3. callback: a function containing the code you want to run that depends on the modules in dependencies. You need to wrap your code in a callback function in order to support asynchronous loading and to be able to use non-global references to the modules.

         回调函数:该函数是基于在依赖参数中加载的模块而编写的你想运行的代码。为了支持异步加载,并且能够使用非全局命名的模块,你必须把你的代码包在回调函数中。

The configuration parameter can simply be omitted, no empty placeholder value is necessary.

配置参数可以省略,不需要空的占位符。

We‘ll cover configuring the loader in more detail below; for now here‘s an example of using the configuration parameter of require:

下面我们将详细介绍加载器配置。下面有一个在require函数中使用配置参数的例子。

1 require({2     baseUrl: "/js/",3     packages: [4         { name: "dojo", location: "//ajax.googleapis.com/ajax/libs/dojo/1.10.4/" },5         { name: "my", location: "my" }6     ]7 }, [ "my/app" ]);

 Here, we’ve changed the configuration slightly to point the dojo package to the Google CDN. Cross-domain loading support is implicit in the AMD format.

在这儿,我们稍微改变了dojo包的一些配置,把包指向了google cdn。AMD模式默认是支持跨域的。

Note that not all configuration options can be set at runtime. In particular, async, tlmSiblingOfDojo, and pre-existing has tests cannot be changed once the loader is loaded. Additionally, most configuration data is shallow copied, which means that you couldn’t use this mechanism to, for example, add more keys to a custom configuration object—the object would be overwritten.

需要注意的是,所有的配置都可以在运行时设置。但,async, tlmSiblingOfDojo以及预先存在的has等模块,一旦通过加载器加载后,就不能再修改了。此外,大多数的配置数据都是浅拷贝,这意味着一个配置对象可能会有多个指针引用着该对象,该对象中的数据随时都与可能被覆写。

Delving Deeper into define 深入了解define函数

The define function accepts the following parameters:

define函数包含了下列参数:

  1. moduleId (optional, default=undefined): a module identifier. This parameter is largely a historical artifact of early AMD loaders or to support pre-AMD Dojo, and should not be provided.

    模块ID(可选参数,默认值=undefined):一个模块标识。该参数基本上为了兼容以前的AMD模式二创建的,一般我们不需要提供。

  2. dependencies (optional, default=[]): an array of module identifiers that are dependencies of your module. If specified, these modules will be resolved before your module is evaluated and they will be passed as parameters to your factory function, in order.
    依赖(可选参数,默认值=[]):你的模块需要引用的模块标识集合。在你的模块主代码执行前,这些依赖模块将被加载,并把参数按照依赖模块顺序传递给工厂函数。
  3. factory: the value of your module, or a "factory" function that will return the value

工厂:该函数是你定义模块的主体,或者改工厂函数会作为返回值返回调用者。

It‘s important to remember that when defining a module, the factory function is only ever invoked once—the returned value is cached by the loader. On a practical level, this means that modules can very easily share objects (similar to static properties in other languages) by loading the same module.

有一点需要注意,当定义一个模块时,工厂函数只会被调用一次-返回值会被加载器缓存。这就意味着,可以通过加载同样模块的方式,共享模块对象(类似于其他开发语言中国的静态属性)。

When defining a module, the value can be given as a plain object:

当定义一个模型,我们可以范围一个普通的对象。

1 // in "my/nls/common.js"2 define({3     greeting: "Hello!",4     howAreYou: "How are you?"5 });

 

Keep in mind that if you do define a module without using a factory function, you won’t be able to reference any dependencies, so this type of definition is rare and usually only gets used by i18n bundles or simple configuration objects.

请记住,如果使用工厂模式定义模块,你就不能访问引用任何依赖。所以这种类型的定义不常用,只会在多语言支持的时候会简单配置对象的时候使用。

How does the loader work?

When you call require to load some modules, the loader has to find the code for the module and then pass it as a parameter to your callback function so you can use it.

当你调用require函数加载一些模块的时候,加载器会先找到你调用模块的代码,然后把模块对应的参数传递到回调函数中。

  1. First the loader has to resolve the module identifier you passed. This involves putting together the baseUrl with the module identifier itself, plus taking into account any modifications required by other configuration options, such as map (discussed later in more detail).

    首先,加载器会解析你加载的模块标识。在解析的时候会把配置的baseurl和模块本身的标识结合在一起,同时兼顾其他配置项配置的参数(例如映射参数)。

  2. At this point the loader has a URL for the module and can load the actual file by creating a new script element on the page and setting the src attribute to the module‘s URL.

    加载器已经有了模块的URL地址,并且可以在页面上添加一个脚本节点,并把模块的URL地址赋值给该脚本节点的src属性。

  3. Once the file is loaded and evaluated, its result is set as the value of the module.

    一旦文件被加载,文件中包含的结果就会被设置成模块的值。

  4. The loader maintains a reference to each module, so the next time the module is requested the loader will return the existing reference.

加载器包含了对每个模块的引用,所以下一步,被加载器加载的模块会返回各自的引用。

When an AMD module is loaded, the code is inserted into a new script element on the page which results in the define function being called. The same process as above happens to load any dependencies passed to define, then the loader‘s reference to your module is set to the value returned by the factory function you passed to define. (If you passed a value, rather than a function to define, then the loader‘s reference to your module is set to that value.)

当一个AMD模块被加载后,在调用define函数被调用的页面上,会添加一个新的脚本节点。

Configuring the loader配置加载器

For legacy compatibility reasons, Dojo‘s loader runs by default in synchronous mode. To put the "A" in "AMD", we need to explicitly configure the loader to run asynchronously. This is done by setting the async configuration property to true:

为了兼容老版本的原因,dojo默认是以同步模式运行的。但在AMD中的A的意思就表明当代的dojoAMD模式都是以异步运行的。在配置中,把async属性设置为true就可以了。

1 <script data-dojo-config="async: true" src="http://www.mamicode.com/js/lib/dojo/dojo.js"></script>

 

You should get in the habit of enabling this as a standard practice - only disable it when you know you need synchronous behavior. The next thing we need to do is configure the loader with information about where our modules are located:

你应该把该配置作为一种习惯,除非你明确的知道你要同步执行某一个动作。下一步要做的事情就是配置本地的模型路径相关的信息。

 1 var dojoConfig = { 2     baseUrl: "/js/", 3     tlmSiblingOfDojo: false, 4     packages: [ 5         { name: "dojo", location: "lib/dojo" }, 6         { name: "dijit", location: "lib/dijit" }, 7         { name: "dojox", location: "lib/dojox" }, 8         { name: "my", location: "my", main: "app" } 9     ]10 };

 Keep in mind you must set the dojoConfig variable before loading dojo.js. Read the Configuring Dojo tutorial if you haven‘t already.

需要注意的是,你必修在加载dojo.js后,再设置dojoConfig。

Let‘s examine the configuration options we‘re using:

让我们检查一下我们配置的选项:

baseUrl (default = the path of the folder dojo.js was loaded from): defines the base URL for loading packages. For example, if you try to load the module "my/widget/Person", the loader will try to load it from:

baseUrl(默认值是加载的dojo.js所在的目录):定义加载包的基础路径。例如你想加载“my/widget/Person”这个模块,加载器会从下面的路径加载。

1 /js/my/widget/Person.js

 

This allows us to place our files wherever is most convenient in the filesystem (in this case, the "js" folder) and still use only the relevant parts of the path in module ids - we don‘t need to require(["js/my/widget/Person"]), we can simply require(["my/widget/Person"]) because we have configured the loader to use "/js/" as a base to prepend to all module ids when actually loading the source file.

这样就可以让我们把我们定义的文件放在合适的物理文件目录下,但只需要引用部分路径作为模块的ID标识即可。我们不需要require(["js/my/widget/Person"]),只需要简单的使用require(["my/widget/Person"])就可以了,这是因为我们已经在加载器中配置了baseUrl属性为"/js/",当加载模块时,所有的模块路径都是使用baseUrl+模块标识的方式获取模块源文件。

tlmSiblingOfDojo (default = true): by default, the loader expects to find modules in folders that are siblings of the folder the loader was loaded from (remember, with Dojo the loader is loaded when your script element loads dojo.js). If your file structure is like this:

tlmSiblingOfDojo:默认情况下,加载器可以查找兄弟目录下的所有模块,并加载,例如:

1 /2     js/3         dojo/4         dijit/5         dojox/6         my/7         util/

 

Then you don‘t need to configure baseUrl or tlmSiblingOfDojo — your top-level modules are siblings of the folder dojo.js was loaded from, so tlmSiblingOfDojo is true.

这样的情况下,你就不需要再配置baseurl和tlmSiblingOfDojo属性了。因为你自定的的模型顶级目录和dojo.js所在的目录为兄弟目录,而模型情况下,tlmSiblingOfDojo的值为true。

packages: an array of package configuration objects. At the most fundamental level, packages are simply collections of modules. dojo, dijit, and dojox are all examples of packages. Unlike a simple collection of modules in a directory, however, packages are imbued with some extra features that significantly enhance module portability and ease-of-use. A portable package is self-contained and also can be installed through tools like cpm. A package configuration allows you to specify:

包:是一组包配置集合。在最基本的层面上,包是一组组模块的集合,例如dojo、dijit、dojox等包。不只是把模块简单的放在一个目录下,包里面还包含了一些其他额外的功能,这可以提高包的可移植性和易用性。包是自包含的,可以通过例如cpm这样的工具进行安装,一个包的配置信息如下:

  • name: the name of the package. This should match the name of the folder that contains the modules.

    name:包的名称。该名字应该和包所在目录的名称一致。

  • location: the location of the package; can either be a path relative to baseUrlor an absolute path. We would like to be able to load modules from the dojo package as "dojo/dom" rather than "lib/dojo/dom" (take another look at the file structure at the beginning of this tutorial), so we specify the location property of the dojo package to be "lib/dojo". This informs the loader that an attempt to load the "dojo/dom" module should load the file "/js/lib/dojo/dom.js" (remember, because of baseUrl "js" will be prepended).

    位置:包的位置,可以是基于baseUrl的相对路径,也可以是绝对路径。我们希望可以通过dojo/dom这样的方式从dojo包中加载模块,而不是通过lib/dojo/dom。所以,我们制定了包的路径为lib/dojo。通过这种模式,当你加载dojo/dom模块时,其实加载时/js/lib/dojo/dom.js文件。

  • main (optional, default = main.js): used to discover the correct module to load if someone tries to require the package itself. For example, if you were to try to require "dojo", the actual file that would be loaded is "/js/dojo/main.js". Since we’ve overridden this property for the "my" package, if someone required "my", they would actually load "/js/my/app.js".

main(可选参数,默认值=main.js):如果有人请求包本身,就指向一个默认的模块文件。例如我们请求dojo,那么该请求指向的实际文件可能是/js/dojo/main.js。例如我们重新了my包的main属性为app,所以在加载是会加载"/js/my/app.js"文件。

  • If we tried to require "util", which is not a defined package, the loader would try to load "/js/util.js". You should always define all of your packages in the loader configuration。

    如果我们请求util,该模块是一个未定义的模块,加载器会尝试加载/js/util.js文件,所以你必须在加载器配置中定义好你需要的所有的包。

Using portable modules 使用轻便的模块

One of the most important features of the new AMD loader is the ability to create fully portable packages. For instance, if you had an application that needed to use modules from two different versions of Dojo, the new loader makes this very easy.

Suppose you have an application built on an older version of Dojo and you want to update to the latest and greatest 1.10 release, but there are some updates to Dojo that render your older code non-functional. You can still update to the current release of Dojo for new code, while using a legacy release of Dojo for you older code. This can be accomplished with the map configuration property:

新的AMD下载机的一个最重要的特性之一就是有能力创建完全轻便的模块包。举个例子,如果你有一个应用必须使用dojo某个模块的不同版本,新的加载器就很容易解决这个问题。

假设你现在有一个应用程序是基于老版本的dojo建设的,现在你想升级到最新版本,也就是1.10版本。但可能有一些更新会让你以前的代码无效了。你依然可以为你新的额代码更新为最新的dojo发布包,同时,为你的旧代码使用dojo之前的版本。这点和通过映射配置完成。

 1 dojoConfig = { 2     packages: [ 3         { name: "dojo16", location: "lib/dojo16" }, 4         { name: "dijit16", location: "lib/dijit16" }, 5         { name: "dojox16", location: "lib/dojox16" }, 6         { name: "dojo", location: "lib/dojo" }, 7         { name: "dijit", location: "lib/dijit" }, 8         { name: "dojox", location: "lib/dojox" }, 9         { name: "myOldApp", location: "myOldApp" },10         { name: "my", location: "my" }11     ],12     map: {13         myOldApp: {14             dojo: "dojo16",15             dijit: "dijit16",16             dojox: "dojox16"17         }18     }19 };

 

AMD高级应用(翻译)