dojo1.7翻译 定义模块(Defining Modules)
原文地址:http://dojotoolkit.org/documentation/tutorials/1.7/modules/
?
dojo现在支持在异步模块异步(AMD)定义中加入模块写入功能了,这使得代码更容易编写和调试。在这一节中,我们学习关于这个模块的使用,并探讨如何使用它。
<script data-dojo-config="async: true" src="js/lib/dojo/dojo.js"></script>?
?async的设置也可以是在dofoConfig对象里设置,但不管怎么做,这个设置必须是在加载dojo之前设置。如果这个被忽略了,他就会使用同步操作。
在异步的模式下,加载器只定义了两种函数。
request 用于加载模块
define 用于定义模块
这里就是与传统的加载器有一个鲜明的对比。以前是加载所有模块。现在是并不是那么做了。
?
下一步就是来配置我们要加载的模块了
?
var dojoConfig = { baseUrl: "/js/", tlmSiblingOfDojo: false, packages: [ { name: "dojo", location: "lib/dojo" }, { name: "dijit", location: "lib/dijit" }, { name: "dojox", location: "lib/dojox" }, { name: "my", location: "my", main: "app" } ]};
?
?在这个配置中,baseUrl被设定为本地目录,目录中包含了我们所有的JavaScript代码,tlmSiblingOfDojo被设定为false,避免混淆不清的行为习惯导致模块包装路径异常。最后是定义包列表,这里util没有被加到包里,虽然也访问util但使用器时对于加载的文件请求解析会有不同。
?
Packages
?
一般来说,包只是模块的集合。dojo,dijit,dojox都是包的实例。然而,不同的目录中的模块的简单集合,包还包含了一些额外的功能,这些功能显着增强了模块的可移植性和易于使用。
?
包有三个主要的配置选项。
name,是包的名字。
location 是包的位置,可以是相对路径的baseURL或绝对路径。
main 是一个可选参数,默认为“main”,如果有人试图要求加载模块,它可以用来发现加载正确的模块文件。例如,如果你尝试要求“dojo”,将装载的是实际的文件"/js/dojo/main.js"。既然我们已经为“我”包重写此属性,如果有人需要“my”,他们将载入“"/js/my/app.js"的。如果我们试图要求加载“util”,这是没有定义的包,装载机会尝试加载"/js/util.js".。
现在,我们已经正确配置加载器,让我们学习如何使用它,先来看require?
?
?
?
Requiring 模块
最好的解释就是例子
require([ "dojo/_base/declare", "dijit/_WidgetBase", "dijit/_TemplatedMixin" ], function(declare, _WidgetBase, _TemplatedMixin){ // "declare" holds the dojo declare function // "_WidgetBase" holds the dijit _WidgetBase constructor // "_TemplatedMixin" holds the dijit _TemplatedMixin constructor // Do whatever you want with these modules here.});?正如你可以看到,require 功能需要两个参数。第一个参数是“依赖关系”的一个集合,第二个参数是一个回调函数。通过列出的顺序解决了模块的依赖。一旦解决了所有的依赖,他们就会传递给回调函数。回调函数是可选的,所以如果你没有做任何功能。只是要加载一些模块,你可以简单地忽略它。如果忽略依赖关系则意为着是另一种操作方案,所以一定要保证有一个依赖在里面,即使它是空的。
require({ baseUrl: "/js/", packages: [ { name: "dojo", location: "//ajax.googleapis.com/ajax/libs/dojo/1.7.1/" }, { name: "my", location: "my" } ]}, [ "my/app" ]);?在这里,我们已经改变了配置,定义dojo包到一个Google的CDN地址。不同于传统的加载模块格式,在异步AMD的格式中是隐含支持跨域加载的。
// in "my/_TemplatedWidget.js"define([ "dojo/_base/declare", "dijit/_WidgetBase", "dijit/_TemplatedMixin" ], function(declare, _WidgetBase, _TemplatedMixin){ return declare([ _WidgetBase, _TemplatedMixin ], {});});?上面的例子,我们通过dojo.declare创建和返回模块。定义模块时要注意的一件重要的事情是,回调函数只调用一次返回值并由装载器缓存。在实践层面,这意味着模块可以很容易地共享对象。
dojo.provide("my._TemplatedWidget");dojo.require("dijit._WidgetBase");dojo.require("dijit._TemplatedMixin");dojo.declare("my._TemplatedWidget", [ dijit._WidgetBase, dijit._TemplatedMixin ], {});?当定义一个模块时,也可以把值定义为一个对象
// in "my/nls/common.js"define({ greeting: "Hello!", howAreYou: "How are you?"});?请记住,如果你定义一个模块,而没有使用一个回调函数,你将不能够引用任何依赖,因此这种类型的定义是不常用的,通常只是在取国际化时使用。
var map16 = { dojo: "dojo16", dijit: "dijit16", dojox: "dojox16" }, dojoConfig = { packages: [ { name: "dojo16", location: "lib/dojo16", packageMap: map16 }, { name: "dijit16", location: "lib/dijit16", packageMap: map16 }, { name: "dojox16", location: "lib/dojox16", packageMap: map16 }, { name: "my16", location: "my16", packageMap: map16 }, { name: "dojo", location: "lib/dojo" }, { name: "dijit", location: "lib/dijit" }, { name: "dojox", location: "lib/dojox" }, { name: "my", location: "my" } ] };?在上面的配置中,任何时候使用map16来引用dojo,dijit,或dojox,他们将重定向到dojo16,dijit16,dojox16,而所有其他的代码将继续正常使用的软件包。
var dojoConfig = { paths: { "my/debugger/engine": "my/debugger/realEngine", "my/debugger": "other/debugger" }};?鉴于此路径的配置,下面是路径解析的替换:
var dojoConfig = { aliases: [ [ "text", "dojo/text" ], [ "dojo/text", "my/text" ], [ "i18n", "dojo/i18n" ], [ /.*\/env$/, "my/env" ] ]};?对应的解析结果text => my/textdojo/text => my/texti18n => dojo/i18nfoo => foo[anything]/env => my/env使用别名时,目标别名必须是绝对的模块标识符,源别名必须是绝对的模块标识符或正则表达式。
// in "my/foo/blah.js"define([ "my/otherModule", "my/foo/bar" ], function(otherModule, bar){ // …});?改成为相对路径的代码
// in "my/foo/blah.js"define([ "../otherModule", "./bar" ], function(otherModule, bar){ // …});?记住相对标识符只能用来指向在相同的包中的模块
// in "my/debug.js"define([ "dojo/dom", "dojo/dom-construct", "dojo/on" ], function(dom, domConstruct, on){ on(dom.byId("debugButton"), "click", function(evt){ require([ "my/debug/console" ], function(console){ domConstruct.place(console, document.body); }); });});?不幸的是,想要完全可移植,"my/debug/console"必须转向到一个相对标识符。只是改变位置路径值是不行的,因为原始模块中已经require过了。为了解决这个问题,dojo加载器提供了一种叫做上下文敏感的require。为了使用其中之一,在初始化定义时,就要通过特殊的模块标识符“require”作为依赖:
// in "my/debug.js"define([ "dojo/dom", "dojo/dom-construct", "dojo/on", "require" ], function(dom, domConstruct, on, require){ on(dom.byId("debugButton"), "click", function(evt){ require([ "./debug/console" ], function(console){ domConstruct.place(console, document.body); }); });});?现在我们就可以使用require去关联到my/debug了
// in "my/Dialog.js"define([ "dojo/_base/declare", "dijit/Dialog", "dojo/text!./templates/Dialog.html" ], function(declare, Dialog, template){ return declare(Dialog, { templateString: template // template contains the content of the file "my/templates/Dialog.html" });});?相对应于以前的版本
dojo.provide("my.Dialog");dojo.require("dijit.Dialog");dojo.declare("my.Dialog", dijit.Dialog, { templateString: dojo.cache("my", "templates/Dialog.html")});?dojo/i18ndojo/i18n替代了dojo.requireLocalization和dojo.i18n.getLocalization
// in "my/Dialog.js"define([ "dojo/_base/declare", "dijit/Dialog", "dojo/i18n!./nls/common"], function(declare, Dialog, i18n){ return declare(Dialog, { title: i18n.dialogTitle });});?相对应于以前的版本代码
dojo.provide("my.Dialog");dojo.require("dijit.Dialog");dojo.requireLocalization("my", "common");dojo.declare("my.Dialog", dijit.Dialog, { title: dojo.i18n.getLocalization("my", "common").dialogTitle});?dojo/has新加载器实现加载了has.js功能
// in "my/events.js"define([ "dojo/dom", "dojo/has!dom-addeventlistener?./events/w3c:./events/ie" ], function(dom, events){ // events is "my/events/w3c" if the "dom-addeventlistener" test was true, "my/events/ie" otherwise events.addEvent(dom.byId("foo"), "click", function(evt){ console.log("Foo clicked!"); });});??相对应于以前的版本代码
dojo.requireIf(window.addEventListener, "my.events.w3c");dojo.requireIf(!window.addEventListener, "my.events.ie");my.events.addEvent(dom.byId("foo"), "click", function(evt){ console.log("Foo clicked!");});?dojo/loaddojo/load和dojo/has很相似,但dojo/load使用了两次模块定义并返回模块加载集合
// in my/loadRenderer.jsdefine([ "dojo/load!./sniffRenderer" ], function (renderer) { // do something with renderer}); // in my/sniffRenderer.jsdefine([], function () { // returns an array of module identifiers to load; // the first module listed is the one returned by dojo/load return [ sniffModuleIdOfRenderer() ];});?但有多个模块被定义时,只取第一个模块。如果没有模块被定义,将标识undefined? 相对应于以前的版本代码
var module = sniffModuleIdOfRenderer();dojo.require(module);var renderer = dojo.getObject(module);// do something with renderer?dojo/domReadydojo/domReady替换了dojo.ready,当只有dom初始化后才会执行模块
// 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.ready(function(){ dojo.byId("someElement");});
// in "my/a.js"define([ "b" ], function(b){ var a = {}; a.stuff = function(){ return b.useStuff ? "stuff" : "things"; }; return a;}); // in "my/b.js"define([ "a" ], function(a){ return { useStuff: true };}); // in "my/circularDependency.js"require([ "a" ], function(a){ a.stuff(); // "things", not "stuff"});?加载器在这种情况下,将尝试加载模块A,模块B,对于模块A和会发现两个模块是一个循环依赖。为了打破循环依赖,模块将被自动解析为一个空的对象。空对象将传递A的值到模块B,然后模块A的回调函数将被调用,其返回值被丢弃。在上面的例子中,这意味着将是一个空的对象,而不是一个功能的对象,所以我们的代码没有如预期般运作。
// in "my/a.js"define([ "b", "exports" ], function(b, exports){ exports.stuff = function(){ return b.useStuff ? "stuff" : "things"; }; return exports;}); // in "my/b.js"define([ "a" ], function(a){ return { useStuff: true };}); // in "my/circularDependency.js"require([ "a" ], function(a){ a.stuff(); // "stuff"});?
// in "my/legacyModule.js"dojo.provide("my.legacyModule");my.legacyModule = { isLegacy: true};?当异步加载器加载到require(["my/legacyModule"]),处理的方式是把对象赋值于my.logacyModule
# node.js:node path/to/dojo.js load=my/serverConfig load=my/app # rhino:java -jar rhino.jar path/to/dojo.js load=my/serverConfig load=my/app?每个load=属性都将自动加入模块依赖。在浏览器中,等价的代码如下
<script data-dojo-config="async: true" src="path/to/dojo.js"></script><script>require(["my/serverConfig", "my/app"]);</script>?结论dojo的异步加载器提供了更多的功能和特性,虽然代码相对有点长。这里只是简短的介绍,想要了解更多内容请查看参考文档。
?