Dojo require()和AMD(1.7)

32

我在转向使用Dojo和新的AMD结构时遇到了很多困难,真的希望有人能为整个概念提供一些启示。我过去几周一直在依靠Google寻找关于使用方式以外的信息,并探究使用这种结构和设计模式趋势的信息。

我觉得奇怪的是,对于一个相对复杂的javascript应用程序,例如需要创建和样式化Dijits、创建DOM元素等主页面,我需要引入并使用大量不同的模块,而这些模块在AMD系统之前都可在dojo命名空间中获得(或者至少没有分配给23个不同的变量)。

例如:

require(['dijit/form/ValidationTextBox', 'dijit/form/SimpleTextarea', 'dijit/form/CheckBox', 'dijit/Dialog', 'dijit/form/Form'])
require(['dojo/ready', 'dojo/parser', 'dojo/dom-style', 'dijit/registry', 'dojo/dom', 'dojo/_base/connect', 'dojo/dom-construct'], 
function(ready, parser, style, registry, dom, event, construct){
    //...etc
}

这只是我正在处理的页面中的一些模块。肯定有更好的、不会在未来版本中破坏的方法来访问这些方法,等等。我的意思是,难道我真的要导入一个全新的模块来使用byId()吗?还需要另一个模块来连接事件吗?除此之外,为了保留参数列表中的变量名而创建的所有混乱看起来都像是回退。

我想也许当需要时只需require()该模块,如query模块,但如果我需要它超过一次,那么它被分配的变量很可能超出范围,我需要把它放在一个domReady!ready调用中。真的好麻烦啊…!

这就是为什么我只能假设是我对Dojo缺乏理解。

我已经尽力查找并购买了书籍(尽管是一本AMD之前的书),但这个库确实让我费尽心思。我欢迎任何人能够帮我解决这个问题。

示例编辑

require(['dijit/form/ValidationTextBox'])
require(['dojo/ready', 'dojo/parser', 'dojo/dom-style', 'dijit/registry', 'dojo/dom', 'dojo/_base/connect', 'dojo/dom-construct'], function(ready, parser, style, registry, dom, event, construct){
    /* perform some tasks */
    var _name = new dijit.form.ValidationTextBox({
        propercase : true,
        tooltipPosition : ['above', 'after']
    }, 'name')

    /*
    Let's say I want to use the query module in some places, i.e. here.
    */
    require(['dojo/query', 'dojo/dom-attr'], function(query, attr){
        query('#list li').forEach(function(li){
            // do something with these
        })
    })
}
根据这种格式,它与许多来自道具工具包人员以及第三方网站的示例一起使用,我认为在第一个function(ready, parser, style, registy...中加载所有必需的模块绝对是荒谬的,因为它会变得越来越长,并会创建命名冲突等问题。
在脚本的生命周期内启动并require()所有需要的模块对我来说似乎很傻。话虽如此,我得看看一些“包管理器”脚本。但是对于这个示例,如果我想在选定的位置使用查询模块,则必须将其与其余内容一起加载到主require()语句中。我在某种程度上理解为什么,但是通用点语法命名空间有什么问题?dojo.whatever?dijit.findIt()?为什么要加载模块、在独特名称中引用、通过闭包传递,等等?
我希望这是一个更容易回答的问题,但我希望这讲得通。
沮丧
叫我新手吧,但这真的...真的...把我逼疯了。当涉及到Javascript时我不是新手(显然不是),但天哪。我真搞不懂!
这就是我所收集到的。在adder.js中:
define('adder', function(require, exports){
    exports.addTen = function(x){
        return x + 10
    }
})

在某些母版页或其他页面:

require(['./js/cg/adder.js'])

虽然不太整洁,但无关紧要。现在重点是使用require(['cg/adder'])格式。

然后,使用adder应该是:

console.log(adder.addTen(100)) // 110

我最接近的结果是console.log(adder) 返回 3。是的,3。否则,就会返回adder未定义

为什么这个要这么难呢? 我在这个问题上犯了新手错误,因为我真的不知道为什么这些代码不能正确运行。

谢谢大家。


你应该提出一个新问题来跟进。在这里,你没有足够的代码向我们展示问题(例如,你甚至没有定义 adder)。 - Domenic
根据您上面的示例,您只需要在单个require语句中使用dijit/form/ValidationTextBox和dojo/query这两个模块依赖项。传递依赖关系已经为您处理好了。就像@Domenic所说的那样,也许我们应该重新开始。 - peller
我不是?嗯,好吧,我就重新开始。谢谢大家。干杯 - Phix
刚读了你的修改后的问题。至少从你提供的例子来看,大多数依赖都是完全不必要的。你需要“require”ValidationTextBox,最好将引用传回给它,而不是在代码中使用全局dijit.form.ValidationTextBox样式引用,ValidationTextBox所需的所有其他内容都会被获取,因为加载程序递归处理依赖关系。有道理吗?实际上,你编写的代码可能存在时间问题,因为你需要ValidationTextBox,然后在另一个语句的回调中使用它。 - peller
2个回答

20

依赖数组的格式:

define(["a", "b", "c"], function (a, b, c) {
});

匹配数组中的元素和函数参数确实很麻烦,容易出错。

我更喜欢使用 require 格式(“简化的 CommonJS 包装器”):

define(function (require) {
    var a = require("a");
    var b = require("b");
    var c = require("c");
});

这样做可以使您的代码行短,允许您重新排列/删除/添加行而不必记得在两个地方更改内容。

后一种格式在PS3和旧版Opera移动浏览器上无法使用,但希望这对您没有影响。


至于为什么要这样做而不是手动命名空间对象,@peller的答案概述了为什么模块化是一件好事,而我在一个类似的问题的回答中谈到了为什么AMD和模块系统作为实现模块化的方式是一件好事。

我想补充一点到@peller的答案中,即“注意依赖关系实际上可以创造出更好的代码。”如果您的模块需要太多其他模块,那就不好。我们在72K LOC JavaScript代码库中有一个宽松的规则,即一个模块应该是大约100行长,并且需要零到四个依赖项。这为我们服务得很好。


另一个有趣的事实是,使用AMD时,当您的模块不再被引用时,您所依赖的代码实际上可以被垃圾回收。如果所有内容都附加到全局对象上,则不会发生这种情况。 - peller
1
另外,请注意Dojo的加载器是异步的(使用异步I/O),因此虽然它支持@Dominic在这里展示的“立即”CJS require签名,但如果其他代码尚未加载模块,则此变体将失败。这就是为什么有一个需要带有数组和回调的require签名。尽管AMD依赖项数组格式很笨拙,但它旨在简化异步加载模块的任务。CJS主要设计用于没有与Web浏览器相同限制的服务器端系统。 - peller
@peller FALSE。Dojo和其他符合AMD标准的加载器将使用Function.prototype.toString来解析工厂函数的主体,然后组装依赖项数组。 - Domenic
@peller 这是 RequireJS 中相关的代码:https://github.com/jrburke/requirejs/blob/master/require.js#L1701-1722这是在 Noble Modules 中(实现了 CommonJS Modules/2.0,也支持异步加载)的代码:https://github.com/NobleJS/Noble-Modules/blob/1b69a0d7284e9b804488424414b0216fa5ded836/nobleModules.js#L620-644 - Domenic
我只是想为Dojo做出断言。Dojo是否支持这个功能?我知道CJS如何搜索函数,但是我的意思是建议AMD专门设计其API以避免这种情况,并考虑到不同的、更明确的方法。最终,requirejs显然做出了妥协。这是否属于AMD规范的一部分? - peller
我找不到Dojo模块的源代码,但如果Dojo声称符合AMD规范,那么它必须是这样的。 - Domenic

12

requirejs.org提供了一个很好的概述,解释了什么是AMD以及为什么要使用它。是的,Dojo正在向更小的模块转移,您可以单独引用这些模块。结果是您加载的代码更少,对它的引用也更明确。我认为注意依赖关系实际上会使代码更好。 AMD使优化成为可能,一旦迁移完成,您就不必再将所有内容加载到全局中了。没有更多的碰撞! require()块包装使用各种模块的代码。 domReady!与DOM的加载有关,与变量的作用域无关。

总之,这偏离了SO的Q&A格式。 您可能希望提出具体问题。


谢谢提供的信息。我会更深入地了解requirejs并继续攻克这个问题。已编辑原始内容 - Phix

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接