原作者:   Kris Zyp

翻译: feijia



在最新的1.6 版本中,Dojo(Core) Dijit 已经进行了代码重构首次引入了符合​CommonsJS AMD API ​规范的异步模块加载机制(AMD)


模块的兼容性

通过重构,现在Dojo的模块已经完全和下列框架兼容:

灵活性, 性能提升 堆栈状态

这次重构给Dojo带来了极好的灵活性,既可以支持传统的同步装载,也可以支持新的基于标签的装载机制。使用新的装载机制会带来很大的性能提升,并且降低了调试的难度(因为你可以看到很清晰的堆栈状态)


匿名模块

AMD 模块规范的另一个重要的优势是它允许定义匿名模块,降低了代码与其所属模块的名称间的耦合。使用匿名模块,模块的名称与身份符合 DRY原则, ​Don’t repeat yourself ​,避免了模块名字在源代码文件名和代码本身中的重复。这种做法使得代码的更容易移植,一段代码可以被放置在任意文件夹或包中而不用修改代码本身。(译者:一个匿名模块在使用时可以随意定义其名字,因此可以很容易的解决模块的重名等冲突情形)。AMD 使用斜杠分割的模块名而不是使用点分割模块名,从而模块路径可以更直接的映射到URL,并允许使用相对路径来引用其他模块。这使得树状文件夹结构也有了移植性. (一个包含一组模块的文件夹可以被移动,改名,而其所包含的模块文件的相对名称不会失效)


向后兼容性

所有基于传统的dojo.provide/dojo.require 的代码将仍然有效。 默认情况下,Dojo使用一个向后兼容的同步加载器,所以你可以继续使用下面的代码:


// 同步加载 dijit.tree
dojo.require ( "dijit.Tree" ) ;
// 加载后 dijit.tree 已经可以使用了
new dijit.Tree ( …) ;


 


使用新的模块加载机制

虽然传统的同步机制和api仍然保留,不过你已经可以在Dojo1.6中尝试使用新的AMD模块加载器来享受 异步加载的好处了 (速度更快,性能更好)

警告: Dojo 1.6 中, 针对AMD的重构仍然属于一个过渡期的改动, 用户自己开发的AMD模块还不能被Dojo的加载器和Build 系统支持. 1.6中现有的编译系统对AMD的支持还非常局限。如果你自己开发了AMD格式的模块,并且你仍然在使用默认的Dojo同步模块加载器,那么你必须严格遵循Dojo模块的格式(包括换行的格式)来保证你自己的模块能够成功编译.


Dojo 1.6 中并未官方支持对于AMD模块的编译, 但是如果你可以遵循Dojo模块的声明风格,编译系统当然也会正常工作。 而由RequireJSBackdraft 提供的编译工具是为AMD专门设计的,因此具有更好的鲁棒性。


虽然Dojo 1.6 并没有正式声明支持用户自定义的AMD模块或异步加载,但是许多用户已经成功的测试过了,并且这无疑是Dojo未来将会正式支持的特性.


创建一个AMD  模块非常简单:

define 方法的第一个参数中 传入一个数组 定义该模块依赖的其他模块

提供一个回调函数,当依赖模块都加载完毕后可以执行你的模块


define([ "dijit/Tree" ] , function ( Tree){
// 该回调函数被执行时,说明 dijit .Tree 已经加载完毕,我们可以用本地变量 Tree 来表示该模块
new Tree( …) ;
}) ;


 

这样定义的模块就已经可以被Dojo异步加载器或RequireJS所使用了。同时你也可以用标准的dojo.require() 来使用。

总结一下在Dojo1.6 中你可以使用的几种加载方式:


  • 用传统的方法(dojo.require()/dojo.provide()) – 这些模块,只能被Dojo同步加载器 加载,但可以被Dojo编译系统(​​Build System​​)正确的编译
  • Dojo同步加载器来加载AMD格式 define()) 模块这些模块可以被正常的加载,并且可以被其他兼容AMD格式的加载器加载. 现在虽然Dojo1.6 还没有正式支持这种用法, 但在目前的Dojo1.6 编译系统中,是可以正常工作的. (前提是你必须严格遵循Dojo模块定义的代码规范)
  • 使用第三方加载器来加载AMD格式(define())模块 模块可以被正常加载,并且可以被其他加载器所使用. 这些模块可以使用RequireJSBackdraft 提供的编译系统正常编译,但是Dojo还没有正式的测试过和其他加载器的兼容性.


模块的引用


当在Dojo中使用AMD格式时,有下列几种方式来引用一个模块.

首先,AMD的模块引用方法是将需要引用的模块声明在依赖模块声明中,并且在回调函数定义时使用相应的参数作为该模块的假名, 像我们在前面声明一个使用dijit.tree的模块那样.  然而,这种做法并不被Dojo 1.6 编译系统所支持.  这种格式只能适用于AMD完全兼容的加载器中。例如:


define([ "dojo/cookie" , "dijit/Tree" ] , function ( cookie, Tree){
var cookieValue = cookie( "cookieName" ) ; // get a cookie
new Tree( …) ; // create a dijit.Tree
}) ;


这样做相对于使用嵌套对象作为命名空间的来引用模块中的对象的传统方法来说有很大优势。因为我们不再需要重复的去键入一个完整的命名空间路径了. (译者: 过去要引用dijit.Tree 的中的某个方法,我们需要每次都使用dijit.tree)而现在我们只需要在依赖定义中给出一次完整的路径 dojo/cookie dijit/tree, 一旦它们被加载,回调函数中我们就可以直接使用该模块的假名来引用它了( 本例中就是cookie, Tree) .


然而, 对于那些需要继续使用Dojo 现有的编译系统,或是希望保留现有的代码风格,或是希望把现有的代码移植到新的AMD格式的人来说, 我们可以将dojo, dijit 声明为依赖模块,这样原有的代码就可以正常工作了。需要注意的是, 即便如此,我们仍然需要列出我们的依赖模块(dojo/cookiet 和dijit/tree),虽然在代码里我们不会直接使用它们了, 例如上面的代码可以改写成


define([ "dojo" , "dijit" , "dojo/cookie" , "dijit/Tree" ] , function ( dojo, dijit){
var cookieValue = dojo.cookie ( "cookieName" ) ; // get a cookie
new dijit.Tree ( …) ; // create a dijit.Tree
}) ;


显然这样的代码有点啰嗦, 但是它使得传统的Dojo代码风格得以保持,因此在移植现有代码时尤为有用


另外需要注意的是, 新的AMD格式的代码模块不能和旧版的Dojo一起工作。 因此你不能直接复制一个Dojo 1.6 的模块到某个过去版本的Dojo 里。 如果你想这样做,你需要手动的去修改这些模块,把它们改成使用dojo.require/dojo.provide API (当然,类似的你要去修改所有被该模块引用的模块)


联合使用DojoRequireJS


Dojo可以在RequireJS上运行,使用RequireJS作为其模块加载器。 首先,加载RequireJS 然后把Dojo配置成一个包:


<mce:script src="require.js" mce_src="require.js"></mce:script>
<mce:script type="text/javascript"><!--
require({"packages":[
{"name":"dojo","location":"packages/dojo","lib":".","main":"./_base/_loader/package-main"}]});
// --></mce:script>


接下来,我们就可以装载自己的应用程序模块了, 该模块使用Dojo作为其依赖


<mce:script type="text/javascript"><!--
require(["my-module"]);
// --></mce:script>


My-module 定义如下:


define([ "dojo" ] , function ( dojo){
dojo.query ( "some-query" ) …

}) ;


在与Dojo一起使用RequireJS时需要注意以下几点:


  • RequireJS在管理模块的加载,因此Dojo 不知道何时去解析HTML中的通过声明方式定义的控件。在页面加载完成,并且你所需要的所有模块都加载完成后,你需要手动的调用dojo.parser.parse()
  • 需要使用RequireJS提供的编译工具


我们会在近期提供如何集成Backdraft Dojo的信息,这项工作很快会开始.


我们还在开发一个包安装工具,它可以自动生成你的配置信息。


我们非常期待看到通过将AMD引入Dojo带来的改进。这也是我们走向包自动管理(autonomous packages)的一个关键步骤。请在近期持续关注我们发布的信息。 如果你也对此感兴趣,请告诉我们。