我们假设这是我的config.js或main.js:
require.config({
// paths are analogous to old-school <script> tags, in order to reference js scripts
paths: {
jquery: "libs/jquery-1.7.2.min",
underscore: "libs/underscore-min",
backbone: "libs/backbone-min",
jquerymobile: "libs/jquery.mobile-1.1.0.min",
jquerymobilerouter: "libs/jquery.mobile.router.min"
},
// configure dependencies and export value aliases for old-school js scripts
shim: {
jquery: ["require"],
underscore: {
deps: ["jquery"],
exports: "_"
},
backbone: {
deps: ["underscore", "jquery"],
exports: "Backbone"
},
jquerymobilerouter: ["jquery", "backbone", "underscore"],
jquerymobile: ["jquery", "jquerymobilerouter", "backbone", "underscore"]
}
});
require(["jquery", "backbone", "underscore", "app/app.min", "jquerymobilerouter", "jquerymobile"], function ($, Backbone, _, App) {
console.log($);
console.log(Backbone);
console.log(_);
$("body").fadeIn(function () {
App.init();
});
});
如果我理解正确,paths
配置选项允许您引用脚本,并在HTML中引用<script>
标记。假设是这种情况,我是否仍然需要在下面的实际require语句中使用$
或下划线和_
等jQuery脚本别名?考虑到如果您使用标准<script>
标记引用jQuery,$
可以在整个脚本中自动使用,这似乎很奇怪。使用paths
我是shim
配置选项的新手,据我所知,已经取代了已弃用的order!
插件。 exports
属性实际上做了什么?它似乎没有为脚本创建别名;例如,如果我将下划线的exports
设置为"whatever"
,然后尝试console.log(whatever)
,那么它是未定义的。那有什么意义呢?
如何在全局范围内正确使用jQuery这样的脚本?也就是说,能够在我的App.js模块或我的“app”文件夹中的任何其他模块中使用$
别名的正确方法是什么?我是否必须在每个单独的模块中使用jQuery并且每次都需要别名$
?或者我在这里以正确的方式完成它的方式?
我也非常感谢对这个特定剧本的任何其他批评;在我看来,Require.js的文档还有很多不足之处;我真的很想知道的事情似乎被掩盖了,让我摸不着头脑。
答案 0 :(得分:22)
路径告诉require.js何时需要依赖项。
例如我的配置如下:
"paths": {
"jquery": "require_jquery"
},
"shim": {
"jquery-cookie" : ["jquery"],
"bootstrap-tab" : ["jquery"],
"bootstrap-modal": ["jquery"],
"bootstrap-alert": ["jquery"]
},
这意味着我每次都在模块中
define( ['jquery']
requirejs从主路径加载文件require_jquery
,而不是尝试加载jquery.js。在您的情况下,它将加载jQuery源文件,然后全局可用。我个人不喜欢这种方法,因为我在require_jquery.js文件中这样做:
define( ["jquery_1.7.2"], function() {
// Raw jQuery does not return anything, so return it explicitly here.
return jQuery.noConflict( true );
} );
这意味着jQuery将仅在我的模块中定义。 (这是因为我编写Wordpress插件,所以我可以包含我自己的jQuery版本,而无需触及外部版本)
导出(从文档中读取应该是您正在使用的模块的名称,以便在加载正确时可以检测到它。Here已解释。所以如果要设置导出对于下划线,它应该是_
jQuery应该是全局的,正如我解释的那样,如果你只是导入它就会执行文件并且jQuery是全局的
编辑 - 回答评论。
是的,我的意思是,您必须为jQuery导出$或jQuery,为主干导出_。根据我从文档中获得的内容,仅在某些边缘情况下才需要这样,并且对于将全局名称空间声明为jQuery的库而言,这不是必需的。
我认为requirejs需要它们才能从CDN加载jQuery。我认为requirejs首先尝试从CDN加载jQuery,然后通过检查“已导出”变量是否存在来进行检查以验证它是否已正确加载,如果不存在,则从本地文件系统加载它(如果你当然已经配置了后备)。当requirejs无法看到404回来时,这是必需的。
jQuery是全球可用的,因为它被声明为全局。如果你只是加载并执行jQuery脚本,你将得到两个全局变量$
和jQuery
(或者你可以像我一样做并避免这种情况)。在define()
函数中,您可以将jQuery别名为您想要的任何内容。
define( [ 'jquery' ], function( jq ) {
// jq is jquery inside this function. if you declared it
// globally it will be also available as $ and jQuery
} );
答案 1 :(得分:22)
为了清除exports
周围的任何混淆,它假设任何填充程序库将属性附加到全局上下文(window
或root
),或者修改已存在的全局属性(例如jQuery插件)。当requireJS获取加载shimmed依赖项的命令时,它会检查与该shim config的exports
值匹配的属性的全局上下文,如果找到它,则将其作为该模块的值返回。如果它找不到它,那么它会加载相关的脚本,等待它执行,然后找到全局符号并返回它。
要记住的一个重要事实是,除非shim配置包含exports
值,否则不会执行该配置上的任何init
方法。依赖性加载器必须在初始化该模块之前找到模块的值(这是exports
指定的值),这就是为什么如果该模块存在垫片init
则需要该属性。 / p>
更新:我还需要指出,如果有问题的模块在任何地方调用define
,那么您对该模块的任何shim配置都将被忽略。这实际上让我有些头疼,因为我想使用shim配置来调用jQuery的jQuery.noConflict(true)
方法来取消jQuery的全局化,并将其范围限定为只需要它的模块,但是不能设法让它运作。 (有关如何使用map config而不是shim config轻松完成此操作的信息,请参阅底部更新。)
更新2:关于requireJS谷歌小组的最近一个问题让我意识到我的解释可能有些误导,所以我想澄清一下。 RequireJS只会重新使用一个shimmed依赖,如果它是通过requireJS加载至少一次。也就是说,如果您只是在主机页面上有一个<script>
标签(例如,下划线),就像这样:
<script src='lib/underscore.js'></script>
<script src='lib/require.js' data-main='main.js'></script>
...你在requireJS配置中有这样的东西:
paths: {
'underscore': 'lib/underscore'
},
shim: {
'underscore': {
exports: '_'
}
}
然后,当您第一次执行define(['underscore'], function (_) {});
或var _ = require('underscore');
时,RequireJS将重新加载下划线库,而不是重新使用先前定义的window._
,因为只要requireJS知道,你之前从未加过下划线。当然,它可以检查是否已在根范围上定义_
,但无法验证已存在的_
是否与在此范围内定义的paths
相同你的prototype
配置。例如,默认情况下,jquery
和window.$
都会将自己分配给 <script src='lib/underscore.js'></script>
<script src='lib/require.js' data-main='main.js'></script>
<script src='lib/underscore.js'></script>
,如果requireJS假定为&#39;窗口。$&#39;是jQuery,当它实际上是原型时,你将会处于糟糕的状态。
所有这些意味着,如果您混合搭配脚本加载样式,那么您的页面将会出现以下内容:
exports
第二个下划线实例是requireJS加载的实例。
基本上,必须通过requireJS加载库,以便requireJS了解它。但是, next 时间你需要下划线,requireJS会go嘿,我已经加载了它,所以只需交回define(['jquery'], function (jQuery) {
值的任何值,不要担心关于加载另一个脚本。&#34;
这意味着您有两个真正的选择。一个是我认为是反模式的:只是不使用requireJS来表达全局脚本的依赖关系。也就是说,只要库将全局附加到根上下文,您就能够访问它,如果没有明确要求该事件,则会发生事件。你可以看到为什么这是一个反模式 - 你基本上只是消除了使用AMD加载器的大部分优势(显式依赖列表和可移植性)。
另一个更好的选择是使用requireJS加载所有内容,以至于您自己应该创建的唯一实际脚本标记是最初加载requireJS的标记。您可以使用填充程序,但95%的情况下,在脚本中添加AMD包装器并不困难。将所有非AMD库转换为AMD兼容可能需要更多的工作,但是一旦完成了一两个,它就会变得容易得多 - 我可以使用任何通用的jQuery插件并将其转换为AMD模块在不到一分钟的时间内完成。它通常只是添加
的问题 return jQuery;
});
位于顶部,
jQuery
在底部。我之所以有这样的原因&#39; jquery&#39;映射到$
而不是(function ($) {
// plugin code here
})(jQuery);
是因为我注意到这些天的大多数插件都包含在这样的闭包中:
$
注意预期的范围是一个好主意。你当然可以映射&#39; jquery&#39;但是直接到jQuery
,假设插件不希望找到$
而不是jQuery.noConflict(true)
。这只是基本的AMD包装器 - 更复杂的包装器通常会尝试检测正在使用哪种加载器(commonJS vs AMD与常规ol&#39;全局)并根据结果使用不同的加载方法。你可以在google上用几秒钟轻松找到这个例子。
更新:我曾经支持使用带有RequireJS的{{1}}的解决方法有效,但是它需要对jQuery源进行非常小的修改,而且我已经想出了一个更好的方法来完成同样的事情而不需要修改jQuery的。幸运的是,RequireJS的作者James Burke也将其添加到RequireJS文档中:http://requirejs.org/docs/jquery.html#noconflictmap