DOJO 之深入分析require

翻译 2017年11月14日 19:47:10

本文翻译自dojo文档

深度分析require

require接收以下参数

    configuration (optional, default=undefined): an object with loader configuration options - this allows you to reconfigure the loader at run-time.
    configuration(configuration;可选;默认=undefined):一个加载配置选项的对象他可以允许你在运行时重新加载配置
    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.
    依赖(undefined;可选;默认=[]):如果指定,这些代码将在运行之前被解析。他们将加载的顺序列出作为参数传递给回调函数。
    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({
    baseUrl: "/js/",
    packages: [
        { name: "dojo", location: "//ajax.googleapis.com/ajax/libs/dojo/1.10.4/" },
        { name: "my", location: "my" }
    ]
}, [ "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的谷歌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和 pre-existing已经确认不可以。除此之外,大多是配置数据是浅拷贝,意味着您不能使用这种机制,例如,添加自定义配置对象它们将被覆盖。

Delving Deeper into define-深入了解define

The define function accepts the following parameters:
define函数支持一下参数
    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.
    moduleId (可选,默认=undefined):一个模块标识符。这个参数是早期历史遗留产物不应该被提供。
    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.
    依赖 (可选,默认=[]):一组模块标识符,表示您的模块依赖关系。如果指定它们将在加载之前被计算,并且按照参数顺序传递到工厂函数之中。
    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:
模块可以使一个无定义对象
// in "my/nls/common.js"
define({
    greeting: "Hello!",
    howAreYou: "How are you?"
});

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.
注意,如果你要使用无工厂函数的模块定义,他将不能够引用任何依赖,这类的定义很少并且只能用在i18n bundles或者简单的配置定义对象上。

How does the loader work?
loader如何工作?
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.
当你使用reqiure来加载模块之后,loader需要去寻找模块的代码并且将它传递给回调的参数使你能够使用。
    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).
    首先locader需要解析传递的模块标识符。这个包括将baseUrl于模块标识符放到一起并加上任何修改所需要的其他配置项,例如map。
    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.
    之后loader具有了模块的URL并且通过添加一个script元素将模块的文件加载进来。src属性获得了模块的url地址。
    Once the file is loaded and evaluated, its result is set as the value of the module.
    一次加载之后,它的结果将被设置为模块的值。
    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.
当AMD模块被加载之后,代码将会被插入到一个新的script元素之中,导致定义函数被调用。
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.)
同样的过程将会在任何依赖被调用的时候执行,loader的引用模块的值就是模块工厂函数的返回值。(如果返回的是值,那么这个模块加载的就是这个值。)


Configuring the loader-配置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的loader默认是以同步模式运行的。如果想加入异步模式我们需要在配置loader的时候加入异步模式调用。者通过设置异步模式调用属性为true得到。

<script data-dojo-config="async: true" src="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:
你应该有让异步调用变为标准操作的习惯只有在需要同步行为的时候才能进行使用。下一步我们应该配置loader告诉它我们的模块在哪里。
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" }
    ]
};

    Keep in mind you must set the dojoConfig variable before loading dojo.js. Read the Configuring Dojo tutorial if you haven't already.
    注意!dojoConfig对象必须在dojo.js 对象之前。
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:/js/my/widget/Person.js
    baseUrl (默认 = dojo.js加载的默认路径):定义了基础的URL用于加载包。例如,如果你尝试加载模块 "my/widget/Person" ,loader 将会尝试从/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.
    它允许我们将我们自己的模块文件放到文件系统中最合适的地方(在这种情况中,是js文件夹)并且使用用路径内的模块。我们不需要require(["js/my/widget/Person"])只需要require(["my/widget/Person"])因为我们的配置对象将"/js/"设置为loader的基础历经并在加载源文件的时候预先考虑所有的模块id。
    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 (默认 = true) :默认情况下,loader期望模块文件夹在dojo文件夹的同级文件夹中。如果你的文件结构如下图。
    //////////////////
    /
        js/
            dojo/
            dijit/
            dojox/
            my/
            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或者baseUrl -你的最高级别模块是和dojo.js文件夹平级的那么tlmSiblingOfDojo是true。
    packages: an array of package configuration objects.
    packages :一组包配置对象。
    At the most fundamental level, packages are simply collections of modules. dojo, dijit, and dojox are all examples of packages.
在最基础的级别,packages是模块的基本集合,dojo, dijit, and dojox 都是包的例子。
     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:
     不同于在目录中一些基本的集合,然而,packages被灌输了一些额外的特性使其能够显著增强模块的可移植性和易用性。一个可移植的包是自包含的并且能够通过像cpm之类的工具进行安装。一个package配置允许你指定。
        name: the name of the package. This should match the name of the folder that contains the modules.
        location: the location of the package; can either be a path relative to baseUrl or 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).
        name :package的名字。这个应该匹配到模块所在文件夹。
        location : package的位置;可以是相对定位也可以是绝对定位。我们希望能够从dojo加载模块包作为"dojo/dom"而不是"lib/dojo/dom"(注意上面提到的文件结构),因此我们指定了对于dojo包的localtions属性"lib/dojo"。这个通知加载程序试图加载"dojo/dom"模块应该加载文件"/ js/lib/dojo/dom.js"(别忘可考虑baseUrl属性 )
        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 package如果有人请求的"my"那么他们应该会加载"/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.
            如果你请求了不是定义package的"util",那么loader会加载"/js/util.js".请在loader configuration中定义你所有的包。



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.
模块可移植是AMD加载器的一个重要的属性。例如,如果你如果你需要引用两个不同的Dojo版本,那么新的loader将会让其变得非常简单。

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:
如果你的应用在dojo的旧版本上建立的你想要将它更新到1.10版本之后,旧版本的一些功能新版本并没有。你仍然可以将他们更新到最新版本,在使用一个旧的dojo版本服务你的老代码同时。这通过map configuration。实现

自己:就是你想让两个版本的dojo兼用用于服务自己的老代码 那么使用map configuration属性。
dojoConfig = {
    packages: [
        { name: "dojo16", location: "lib/dojo16" },
        { name: "dijit16", location: "lib/dijit16" },
        { name: "dojox16", location: "lib/dojox16" },
        { name: "dojo", location: "lib/dojo" },
        { name: "dijit", location: "lib/dijit" },
        { name: "dojox", location: "lib/dojox" },
        { name: "myOldApp", location: "myOldApp" },
        { name: "my", location: "my" }
    ],
    map: {
        myOldApp: {
            dojo: "dojo16",
            dijit: "dijit16",
            dojox: "dojox16"
        }
    }
};

What's going on here?
以上的配置进行了什么?
    (lines 3-5) First we define 3 packages that point to folders containing a legacy release of Dojo
    (lines 3-5) 首先定义了三个packages他们指定了文件夹包括旧版本的dojo。
    (lines 6-8) Next we define 3 packages for the current release of Dojo
    (lines 6-8) 接下来我们定义了三个packages给最新版本的Dojo
    (lines 9-10) We define packages for our old and current code
    (lines 9-10) 我们定义了我们自己的代码(使用老dojo和新dojo的)
    (lines 12-18) We define a map configuration: it applies to the "myOldApp" module, and maps requests for modules from the "dojo", "dijit", and "dojox" packages to "dojo16", "dijit16", and "dojox16", respectively.
    Modules from the "my" package that load modules from dojo, dijit, dojox will get modules from the current Dojo release.
    (lines 12-18) 我们定义了一个map configuration:它将"myOldApp"于 老版本的Dojo的三个主要模块(dojo,dijit,dojox)关联起来."my"包将会从新版本的dojo加载代码。
更多参考请见 AMD Configuration documentation。
You can refer to the AMD Configuration documentation for more information about map.

If you are already familiar with the loader, specifically the packageMap property, it is deprecated - map is the configuration option to use moving forward.





Writing portable modules写可移植的模块

You can (and should) ensure that modules within packages you create always load files from within the same package by specifying dependencies with relative module identifiers. Given the following code in a module in the "my" package:
我们应该确认包内的文件应该通过相对路径加载包内的文件。我们看"my"包这个例子。

// in "my/widget/NavBar.js"
define([
    "dojo/dom",
    "my/otherModule",
    "my/widget/InfoBox"
], function(dom, otherModule, InfoBox){
    // …
});

Instead of explicitly requesting modules from the my package, use relative module identifiers instead:
抛去使用直接引用使用相对路径引用包:
// in "my/widget/NavBar.js"
define([
    "dojo/dom",
    "../otherModule",
    "./InfoBox"
], function(dom, otherModule, InfoBox){
    // …
});

Relative to "my/widget/NavBar":
与"my/widget/NavBar"相关
    "dojo/dom" is in a separate package, so we use the full identifier
    "dojo/dom"是一个独立的包,我们应该使用完整的标识符。
    "my/otherModule" is one directory up, so we use "../"
    "my/otherModule"不在同一目录下应该使用"../"
    "my/widget/InfoBox" is in the same directory, so we use "./"
    "my/widget/InfoBox"在同一目录下应该使用"./"
        If you just specify "InfoBox" it is interpreted as a package name, so you must start the identifier with "./".
        如果你只使用了"InfoBox"那么他会被解释为一个包名,因此你应该在包名前加上"./"。
    Keep in mind that relative identifiers can only be used to refer to modules within the same package. Relative module ids are also only valid when defining a module - they do not work in the dependency list passed to require.
    注意:相对路径标识符只能在同一根目录下使用。相对模块id只有在定义模块时管用,在require时不管用。
Given the same-package restriction of relative identifiers, look back up at the map example — do you notice something wrong? For simplicity's sake, we focused on the aspects of the configuration that enabled one part of your app to use an old release of Dojo and other parts of your app to use a current release. However, we left out something important - Dijit depends on Dojo, and DojoX depends on both Dojo and Dijit. The configuration below will ensure that those dependencies are resolved correctly. For safety's sake, we've also mapped the Dojo packages to themselves (map: { dojo16: { dojo: "dojo16" } }) in case any of the modules failed to use relative identifiers.

考虑到同一个包标识符的限制,回顾上一个map的例子。有一个问题。就是我们注意到了map配置能够让你的app使用dojo的旧版代码。但是您考虑到木有Dijit以来Dojo,DojoX依赖Dojo 和 Dijit。下面的配置能够确保map映射正确。安全起见,我们应该映射(mapped)Dojo包到自身(map: { dojo16: { dojo: "dojo16" } }) 以防包内部的相对引用失败。
==========================================================
var map16 = {
    dojo: "dojo16",
    dijit: "dijit16",
    dojox: "dojox16"
};

dojoConfig = {
    packages: [
        { name: "dojo16", location: "lib/dojo16" },
        { name: "dijit16", location: "lib/dijit16" },
        { name: "dojox16", location: "lib/dojox16" },
        { name: "dojo", location: "lib/dojo" },
        { name: "dijit", location: "lib/dijit" },
        { name: "dojox", location: "lib/dojox" },
        { name: "myOldApp", location: "myOldApp" },
        { name: "my", location: "my" }
    ],
    map: {
        dojo16: map16,
        dijit16: map16,
        dojox16: map16,
        myOldApp: map16
    }
};
=========================================================

Conditionally requiring modules-附有条件的引用模块

Sometimes, you may want to require a module conditionally in response to some condition. For example, you may want defer loading an optional module until an event occurs. This is pretty simple if you’re using explicit module definitions:
有时候你想在某种情况下reqiure一个模块。例如,你有时想要延迟加载一个模块当某个时间发生的时候。这个十分简单当你使用显示模块定义的时候:
===============================================================
define([
    "dojo/dom",
    "dojo/dom-construct",
    "dojo/on"
], function(dom, domConstruct, on){
    on(dom.byId("debugButton"), "click", function(){
        require([ "my/debug/console" ], function(console){
            domConstruct.place(console, document.body);
        });
    });
});
===============================================================
Unfortunately, to be completely portable, that "my/debug/console" needs to be turned into a relative identifier. Just changing it doesn’t work, however, because the context of the original module is lost by the time require is called. In order to resolve this problem, the Dojo loader offers something called a context-sensitive require. In order to use one of these, pass the special module identifier "require" as a dependency in your initial define call:
不幸的是如果你想要完全的可移植性,"my/debug/console"应该使用相对路径,但是修改不会见效,然鹅,因为上下文的原始模块已经丢失。为了让问题得到解决,dojo提供了一个上下文敏感的reqiure。目的是为了使用其中的一个,将特殊的模块标示"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(){
        require([ "./debug/console" ], function(console){
            domConstruct.place(console, document.body);
        });
    });
});


Now, the inner require call uses the locally bound, context-sensitive require function, so we can safely require modules relative to "my/debug".
现在,我们可以放心的使用"my/debug"这个相对路径了,内部reqire使用了本地绑定,上下文敏感的reiqure功能。
    How was require's context lost?
    为什么require上下文会丢失?
    Remember that require is a globally defined function. When the handler for the "click" event executes, the only context it gets from the module it was defined in is the scope. It doesn't know what module it was defined in. There's no "require" in the local scope, so the "require" defined in the global scope is called. Recalling the file system structure referenced throughout this tutorial, if we pass "./debug/console" to require, it will attempt to load the file "/js/debug/console.js", which does not exist. By using the context-sensitive require, we have a local reference to a modified require function that maintains the context of the module, so it correctly loads "/js/my/debug/console.js".
    require是一个全局定义的函数。当处理点击时间的时候,他能够获取的作用于是在时间处理函数内的,他不知道应用了什么模块。因为在该作用域下没有require定义。全局环境下的require没有被调用。当我们调用"./debug/console"的时候会直接调用"/js/debug/console.js",这个位置是不存在的。通过使用上下文敏感的require,我们使用了一个本地引用来指引require函数维持模块的作用于,让其直接操作"/js/my/debug/console.js".
    我说的:就是作用域不同。


Context-sensitive require is also very useful for loading resources (images, templates, CSS) for a module. Given the following file system structure:
上下文敏感的require在模块的加载资源时也是非常有用的,举例,在下面的文件系统结构中
/
    js/
        my/
            widget/
                InfoBox.js
                    images/
                        info.png

Within InfoBox.js we can call require.toUrl to get a complete URL referencing "info.png" that can be set as the src property on an img element.
在InfoBox.js下我们调用require.toUrl生成一个"info.png"的url用其向src设置img的元素。

// in my/widget/InfoBox.js
define([
    "dojo/dom",
    "require"
], function(dom, require){
    // assume DOM structure where #infoBoxImage is an img element
    dom.byId("infoBoxImage").src = require.toUrl("./images/info.png");
});
======================================================

Handling circular dependencies-处理循环的依赖

When you’re writing code, you may occasionally come across cases where you have two modules that need to refer to each other, and this reference creates a circular dependency. In order to resolve a circular dependency like this, the loader immediately resolves the module that recurses first. For example, given the following example:
当你在写代码的时候,你可能遇到两个模块相互引用的情况,这就产生了循环引用。为了解决这个问题,loader 首先加载模块进行递归。例如:


// in "my/moduleA.js"
define([ "./moduleB" ], function(moduleB){
    return {
        getValue: function(){
            return "oranges";
        },

        print: function(){
            // dependency on moduleB
            log(moduleB.getValue());
        }
    };
});

// in "my/moduleB.js"
define([ "./moduleA" ], function(moduleA){
    return {
        getValue: function(){
            // dependency on moduleA
            return "apples and " + moduleA.getValue();
        }
    };
});

// in "index.html"
require([
    "my/moduleA"
], function(moduleA) {
    moduleA.print();
});

View Demo
观察示例!
This looks like it should print "apples and oranges", but instead you get an error in moduleB: Object has no method 'getValue'. Let's take a look at what the loader will do when you load and run "index.html":
这个的结果看起来像"apples and oranges",但是会返回一个moduleB的错误,Object has no method 'getValue'。我们来分析一下loader会做什么。
    Resolve the dependencies passed to require (in index.html): moduleA
    解析依赖并传递给require:moduleA
    Resolve moduleA's dependencies: moduleB
    解析moduleA的依赖:moduleB
    Resolve moduleB's dependencies: moduleA
    解析moduleB的依赖:moduleA
    Detect that it is currently in the process of trying to resolve moduleA
    判断现在需要对moduleA进行判断。

    Break out of the circular dependency by temporarily resolving moduleA as an empty object.
    突破循环依赖通过将模块A解析为一个空对象。
    Resume resolving moduleB by calling its factory function; the empty object will be passed to the factory function as moduleA.
    恢复解析模块B通过调用工厂函数,空对象将会被传递给工厂函数(模块A的)
    Set the loader's reference to moduleB to the return value of the factory function.
    将模块B的值返回给loader
    Resume resolving moduleA by calling its factory function.
    解析模块A的值
    Set the loader's reference to moduleA to the return value of the factory function — while the loader now refers to the valid value; moduleB is left still referring to the empty object.
    将loader的对于模块A的参照指向工厂函数的返回值-当loader现在指向一个有效的值。模块B现在仍然指向空对象。
    Execute moduleA.print — since moduleB has a bad reference to moduleA, when it calls moduleA.getValue an error is thrown.
    解析模块A的print - 由于模块A在B对象中是一个空值所以抛出异常。

To solve this problem, the loader provides a special "exports" module identifier. When used, this module will return a reference to a persistent object representing the module being defined — the object will initially be empty, but any modules involved in circular reference resolution will be passed a reference to it. The same reference will be passed into the module that has listed "exports" in its dependencies. With the reference to this persistent object, we can define our properties directly on the object. The sequence of events here can be a little difficult to follow, so take a look at the updated code below and the explanation that follows.
为了解决这个问题loader提供了一个特别的"exports"模块。当使用之后,模块会返回一个参照给一个持久对象,来表达被定义的模块,这个对象最初是空的,但是任何模块任何参与循环引用的模块都会传递一个引用到这里。这些引用将会被传递到那些列出了"exports"的依赖中。由于持久对象中的引用,我们可以直接定义我们的属性。现在看一下下面的代码注意顺序


// in "my/moduleA.js"
define([ "./moduleB", "exports" ], function(moduleB, exports){
    exports.getValue = function(){
        return "oranges";
    };

    exports.print = function(){
        log(moduleB.getValue());
    };
});

// in "my/moduleB.js"
define([ "./moduleA" ], function(moduleA){
    return {
        getValue: function(){
            return "apples and " + moduleA.getValue();
        }
    };
});

// in "index.html"
require([
    "my/moduleA"
], function(moduleA) {
    moduleA.print();
});

View Demo
观察例子:

What happens now when you load and run "index.html":

    Resolve the dependencies passed to require (in index.html): moduleA
    解析模块将其传递给require:"exports"
    Resolve moduleA's dependencies: moduleB
    解析模块A的依赖 : moduleB
    Resolve moduleB's dependencies: moduleA
    解析模块B的依赖 :moduleB
    Detect that it is currently in the process of trying to resolve moduleA.
    判断现在是要解析模块A。
    Break out of the circular dependency by temporarily resolving moduleA as an empty object.
    将模块A设置为空对象来打破循环引用。
    Resume resolving moduleB by calling its factory function; the empty object will be passed to the factory function as moduleA.
    解析模块B通过调用工厂函数;模块A作为空对象传入引用。
    Set the loader's reference to moduleB to the return value of the factory function.
    模块B作为通过工厂函数返回值给loader。
    Resume resolving moduleA by calling its factory function — the empty object that has been created as a placeholder for moduleA will be passed to the factory function as the exports parameter.
    解析模块A-空的对象已经被创建作为一个占位符,模块A将会被传递给工厂函数作为一个exports参数

    After resolving a module that has listed "exports" as a dependency, the loader's reference to the module is not set to the factory function's return value. Rather, the loader assumes the module set any necessary properties on the empty object that was created as a placeholder and passed to the factory function as the exports parameter.
    通过解析那些将"exports"作为依赖的参数,loader的引用不会被作为工厂函数的返回值。相反,loader假定模块设置了任何必要的属性在空对象上,他们作为一个占位符并且通过工厂函数作为exports参数被传递。
    Execute moduleA.print — since moduleB has a valid reference to the object that was eventually populated by moduleA, when it calls moduleA.getValue it works as expected.
    执行 moduleA.print函数- 因为moduleB有一个有效的引用最终被模块A填充,当他调用模块A是moduleA.getValue能够被调用。

It is important to keep in mind that although using exports provides a reference that is eventually valid, it's still just an empty object at the time the dependent module (moduleB) is resolved. When your factory function (for moduleB) is executed, it receives a reference to an empty object (for moduleA). It is only after the circular dependency has been fully resolved (moduleA is temporarily resolved as {}, moduleB is resolved, then moduleA is fully resolved) that the object is updated with the module's (moduleA) methods and properties, which will then be available to functions defined in your factory function (for moduleB), but called later. The following code demonstrates this distinction:

很重要的一点!当模块B被解析的时候模块A还是一个空对象。当工厂函数执行,他接受了一个空的对象引用。当你的工厂函数执行之后(从模块B),他接收了一个引用(模块A的空引用)。只有在循环依赖已经完全被加载之后(模块A临时被解析为{}),模块B被解析之后模块A被完全解析)模块的属性和方法才会被完全解析,模块中被引用的方法才会有效,但是被调用的晚些。下面的代码将论证!

// in "my/moduleA.js"
define([ "./moduleB", "exports" ], function(moduleB, exports){
    exports.isValid = true;

    exports.getValue = function(){
        return "oranges";
    };

    exports.print = function(){
        // dependency on moduleB
        log(moduleB.getValue());
    }
});

// in "my/moduleB.js"
define([ "./moduleA" ], function(moduleA){
    // this code will run at resolution time, when the reference to
    // moduleA is an empty object, so moduleA.isValid will be undefined
    if(moduleA.isValid){
        return {
            getValue: function(){
                return "won't happen";
            }
        };
    }

    // this code returns an object with a method that references moduleA
    // the "getValue" method won't be called until after moduleA has
    // actually been resolved, and since it uses exports, the "getValue"
    // method will be available
    return {
        getValue: function(){
            return "apples and " + moduleA.getValue();
        }
    };
});

// in "index.html"
require([
    "my/moduleA"
], function(moduleA) {
    moduleA.print();
});

View Demo
============================================================

Loading non-AMD code-加载非AMD代码

As mentioned in the section on module identifiers, the AMD loader can also be used to load non-AMD code by passing an identifier that is actually a path to a JavaScript file. The loader identifies these special identifiers in one of three ways:
AMD可以加载非AMD代码通过传递一个标识符,来表示一个js代码的文件位置。有一下三种方式
    The identifier starts with a “/”
    1 以 “/”开头
    The identifier starts with a protocol (e.g. “http:”, “https:”)
    2 以 协议开头例如(e.g. “http:”, “https:”)
    The identifier ends with “.js”
    3 以 ".js"结尾

When arbitrary code is loaded as a module, the module’s resolved value is undefined; you will need to directly access whatever code was defined globally by the script.
当代码被加载之后,模块的解析值为 undefined 你可以尝试直接访问代码的全局定义。

One feature exclusive to the Dojo loader is the ability to mix-and-match legacy Dojo modules with AMD-style modules. This makes it possible to slowly and methodically transition from a legacy codebase to an AMD codebase instead of needing to convert everything immediately. This works both when the loader is in sync mode and when it is in async mode. When in async mode, the resolved value of a legacy module is whatever object exists in the global scope that matches the file’s first dojo.provide call once the script is done being evaluated. For example:
Dojo还有一个特性就是你可以老式和新式用法混用。这样可以使得代码慢慢的进行风格转换。再同步异步风格之下都可以。处于异步模式时,旧的模块的解析值是全局范围内存在的任何对象,一旦脚本完成解析,该对象将与文件的第一个dojo.provide调用相匹配。


// in "my/legacyModule.js"
dojo.provide("my.legacyModule");
my.legacyModule = {
    isLegacy: true
};

When loading this code via the AMD loader through a call to require(["my/legacyModule"]), the resolved value of this module will be the object assigned to my.legacyModule.

当通过调用require(["my/legacyModule"])通过AMD加载器加载这个代码时,这个模块的解析值将是分配给my.legacyModule的对象
====================

Server-side JavaScript-服务器端的js

One final feature of the new AMD loader is the ability to load JavaScript on the server using either node.js or Rhino. Loading Dojo via command-line looks like this:

# 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

See the Dojo and Node.js tutorial for more details.

Each load= arguments add modules to a dependency list that is automatically resolved once the loader is ready. In a browser, the equivalent code would look like this:

<script data-dojo-config="async: true" src="path/to/dojo.js"></script>
<script>require(["my/serverConfig", "my/app"]);</script>

Conclusion

The new AMD format brings many exciting new features and capabilities to Dojo; despite its length, this tutorial gives only a very brief overview of everything that the new loader has to offer. To learn more details about all of the new features of the AMD loader, be sure to check out the Dojo loader reference guide.
 the Dojo loader reference guide.  http://dojotoolkit.org/reference-guide/1.10/loader/

相关文章推荐

dojo require provider registerPath

转自:http://www.okajax.com/a/200807/0HW9522008_6.html package机制 说完了dojo里的类继承机制,不得不说说package机制。 ...
  • dawn_cx
  • dawn_cx
  • 2013年12月23日 14:40
  • 540

理解dojo.require机制

Dojo 提供了一个非常强大的javascript控件库. 在使用dojo之前,用户基本上不需要具备任何基础知识. 你可以用script远程链接到dojo(dojo.js), 也可以把dojo.js下...

dojo.require 详解

Dojo 提供了一个非常强大的javascript控件库. 在使用dojo之前,用户基本上不需要具备任何基础知识. 你可以用script远程链接到dojo(dojo.js), 也可以把dojo.js下...

dojo.require()

Dojo 提供了一个非常强大的JavaScript控件库. 在使用dojo之前,用户基本上不需要具备任何基础知识. 你可以用script远程链接到dojo(dojo.js), 也可以把dojo.j...

让dojo.require异步加载小部件

注:本文的技术实现是基于非AMD方式的。 最近用dojo开发了个系统,大约有40个widget,之前的做法是在首页里面一开始就通过dojo.require将这40多个小部件引入了,由于是在本机测试,所...

深入分析Java+Web技术内幕PDF

  • 2017年11月02日 16:32
  • 876KB
  • 下载

Spring MVC 教程,快速入门,深入分析

作者:赵磊 博客:http://elf8848.iteye.com   目录 一、前言 二、spring mvc 核心类与接口 三、spring mvc 核心流程图 四、sprin...

uCOS任务堆栈的深入分析

  • 2014年11月17日 10:05
  • 29KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:DOJO 之深入分析require
举报原因:
原因补充:

(最多只允许输入30个字)