javascript Require.js 正在伤害我的大脑。关于加载脚本/模块的方式的一些基本问题
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11031485/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Require.js is hurting my brain. Some fundamental questions about the way it loads scripts/modules
提问by J. Ky Marsh
Let's assume this is my config.js or main.js:
让我们假设这是我的 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();
});
});
If I understand correctly, the
paths
config option allows you to reference scripts, a-la the<script>
tag within HTML. Assuming this is the case, do I still need to alias scripts like jQuery with a$
or underscore with a_
in my actual require statement below? It seems strange that I'd have to, given that if you reference jQuery with a standard<script>
tag,$
can be used throughout your script automatically. Shouldn't it be the same using thepaths
?I'm new to the
shim
config option, which I understand has replaced the deprecatedorder!
plugin. What does theexports
property actually DO? It doesn't seem to create an alias for a script; for example, if I set theexports
for underscore to"whatever"
, and then try toconsole.log(whatever)
, it's undefined. So what's the point?How would scripts like jQuery be properly used "globally?" That is, what's the proper way to be able to use the
$
alias within my App.js module, or any other module in my "app" folder? Do I have to require jQuery within every individual module and alias$
every single time? Or is the way I've done it here the proper way?
如果我理解正确,
paths
配置选项允许您引用脚本,即<script>
HTML 中的标记。假设是这种情况,我是否仍然需要在下面的实际 require 语句中使用 a$
或下划线为jQuery 等脚本添加别名_
?我不得不这样做似乎很奇怪,因为如果您使用标准<script>
标记引用 jQuery ,$
则可以自动在整个脚本中使用它。使用 不应该是一样的paths
吗?我是
shim
config 选项的新手,我知道它已经取代了已弃用的order!
插件。该exports
物业实际上有什么作用?它似乎没有为脚本创建别名;例如,如果我将exports
for 下划线设置为"whatever"
,然后尝试为console.log(whatever)
,则它是未定义的。那么有什么意义呢?如何“全局”正确使用像 jQuery 这样的脚本?也就是说,能够
$
在我的 App.js 模块或“app”文件夹中的任何其他模块中使用别名的正确方法是什么?我是否每次都必须在每个单独的模块和别名中$
都需要 jQuery ?或者我在这里做的方式是正确的吗?
I'd greatly appreciate any other criticisms of this particular script as well; the documentation for Require.js, in my opinion, leaves much to be desired; things I'd really like to know more about seem to get glossed over and leave me scratching my head.
我也非常感谢对这个特定脚本的任何其他批评;在我看来,Require.js 的文档还有很多不足之处;我真的很想知道更多的事情似乎被掩盖了,让我摸不着头脑。
采纳答案by Nicola Peluchetti
Paths tell require.js where to look when you require that dependency.
For example i have things configured like this:
"paths": { "jquery": "require_jquery" }, "shim": { "jquery-cookie" : ["jquery"], "bootstrap-tab" : ["jquery"], "bootstrap-modal": ["jquery"], "bootstrap-alert": ["jquery"] },
this means that every time in a module I do
define( ['jquery']
requirejs loads the file
require_jquery
from the main path instead of trying to load jquery.js. In your case it would load the jQuery source file, which would then be globally available. I personally don't like that approach and for that reason in the require_jquery.js file I do:define( ["jquery_1.7.2"], function() { // Raw jQuery does not return anything, so return it explicitly here. return jQuery.noConflict( true ); } );
which means that jQuery will be defined only inside my modules. (This is because i write Wordpress plugins and so I can include my own version of jQuery without touching the outside version)
Exports (reading from the docs simply should be the name of the module you are using so that it can be detected if loading went correctly. Hereis explained. So if you want to set an export for underscore it should be
_
jQuery should be global as I explained, if you simply import it the file is executed and jQuery is global
路径告诉 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 版本)
导出(从文档中读取的应该只是您正在使用的模块的名称,以便可以检测到加载是否正确。这里有解释。所以如果你想为下划线设置导出,它应该是
_
正如我所解释的,jQuery 应该是全局的,如果你只是导入它,文件就会被执行并且 jQuery 是全局的
EDIT - to answer the comments.
编辑 - 回答评论。
yes i mean that, you must export $ or jQuery for jQuery and _ for backbone. From what i got from the docs this is needed only in some edge cases and would not be necessary for libraries that declare themselves in the global namespace as jQuery.
I think that requirejs needs them when it has to fallback from loading jQuery from a CDN. i think that requirejs first tries to load jQuery from the CDN, then makes a check to verify that it was loaded correctly by checking that the "exported" variable exists, and if it doesn't it loads it form the local filesystem (if you had configured fallbacks, of course). This is something that it's needed when requirejs can't see a 404 coming back.
jQuery is globally available because it's declared global. If you simply load and execute the jQuery script, you will end up with two globals,
$
andjQuery
(or you can do as i did and avoid that). Inside thedefine()
function you can alias jQuery to be whatever you want.define( [ 'jquery' ], function( jq ) { // jq is jquery inside this function. if you declared it // globally it will be also available as $ and jQuery } );
是的,我的意思是,您必须为 jQuery 导出 $ 或 jQuery,为主干导出 _。从我从文档中获得的信息来看,这仅在某些边缘情况下才需要,而对于在全局命名空间中将自身声明为 jQuery 的库则不需要。
我认为当需要从 CDN 加载 jQuery 时,requirejs 需要它们。我认为 requirejs 首先尝试从 CDN 加载 jQuery,然后通过检查“exported”变量是否存在来验证它是否正确加载,如果不存在,则从本地文件系统加载它(如果你当然,已经配置了回退)。这是当 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 } );
回答by Isochronous
Just to clear up any confusion around exports
, it's assumed that any shim library attaches a property to the global context (window
or root
), or modifies an already-existing global property (e.g. a jQuery plugin). When requireJS gets the command to load a shimmed dependency, it examines the global context for a property matching the exports
value of that shim config, and if it finds it, returns it as the value of that module. If it doesn't find it, then it loads the associated script, waits for it to execute, then finds the global symbol and returns it.
为了消除围绕 的任何混淆exports
,假设任何 shim 库将属性附加到全局上下文(window
或root
),或修改已存在的全局属性(例如 jQuery 插件)。当 requireJS 获得加载 shim 依赖项的命令时,它会检查全局上下文以exports
查找与该 shim 配置的值匹配的属性,如果找到它,则将其作为该模块的值返回。如果没有找到,则加载相关的脚本,等待它执行,然后找到全局符号并返回它。
An important fact to remember is that unless the shim config contains an exports
value, any init
method on that config will NOT be executed. The dependency loader must locate a value for the module (which is what exports
specifies) before that module can be initialized, which is why the property is required if there is a shim init
for that module.
要记住的一个重要事实是,除非 shim 配置包含一个exports
值,init
否则不会执行该配置上的任何方法。依赖加载器必须exports
在该模块被初始化之前为该模块定位一个值(这是指定的值),这就是为什么如果init
该模块有一个 shim 则需要该属性。
update: I also need to point out that if the module in question calls define
anywhere, any shim config you have for that module will be ignored. This actually caused me some headaches because I wanted to use the shim config to call jQuery's jQuery.noConflict(true)
method to un-globify jQuery and keep it scoped to just the modules that require it, but couldn't manage to get it working. (See update at bottom for info on how to easily do this using map config instead of shim config.)
更新:我还需要指出,如果有问题的模块在define
任何地方调用,您对该模块的任何垫片配置都将被忽略。这实际上让我有些头疼,因为我想使用 shim 配置来调用 jQuery 的jQuery.noConflict(true)
方法来取消jQuery 的全局化,并将它的范围限制在需要它的模块上,但无法让它工作。(有关如何使用地图配置而不是垫片配置轻松执行此操作的信息,请参阅底部的更新。)
update 2: A recent question on the requireJS google group made me realize that my explanation might be slightly misleading, so I'd like to clarify. RequireJS will only re-use a shimmed dependency if it was loaded via requireJS at least once. That is to say, if you simply have a <script>
tag on the hosting page (say, for example, underscore), like this:
更新 2:最近关于 requireJS google group 的一个问题让我意识到我的解释可能有点误导,所以我想澄清一下。如果通过 requireJS 加载了至少一次,RequireJS 只会重新使用一个 shimmed 依赖项。也就是说,如果您只是<script>
在托管页面上有一个标签(例如,下划线),如下所示:
<script src='lib/underscore.js'></script>
<script src='lib/require.js' data-main='main.js'></script>
...and you have something like this in your requireJS config:
...你的 requireJS 配置中有这样的东西:
paths: {
'underscore': 'lib/underscore'
},
shim: {
'underscore': {
exports: '_'
}
}
Then the first time you do define(['underscore'], function (_) {});
or var _ = require('underscore');
, RequireJS will re-load the underscore library rather than re-using the previously defined window._
, because as far as requireJS knows, you never loaded underscore before. Sure, it can check to see if _
is already defined on the root scope, but it has no way of verifying that the _
that's already there is the same as the one defined in your paths
config. For example, both prototype
and jquery
assign themselves to window.$
by default, and if requireJS assumes that 'window.$' is jQuery when it is in fact prototype, you're going to be in a bad situation.
那么第一次执行define(['underscore'], function (_) {});
or 时var _ = require('underscore');
,RequireJS 会重新加载下划线库,而不是重新使用之前定义的window._
,因为就 requireJS 而言,您之前从未加载过下划线。当然,它可以检查是否_
已经在根范围内定义,但它无法验证_
已经存在的是否与您的paths
配置中定义的相同。例如,无论是prototype
和jquery
分配自己window.$
默认的,如果requireJS假设“窗口。$”是jQuery的时候它实际上是原型,你要在一个糟糕的情况。
All of that means that if you mix-and-match script loading styles like that, your page will wind up with something like this:
所有这一切都意味着,如果您像这样混合搭配脚本加载样式,您的页面最终将是这样的:
<script src='lib/underscore.js'></script>
<script src='lib/require.js' data-main='main.js'></script>
<script src='lib/underscore.js'></script>
Where the second underscore instance is the one loaded by requireJS.
其中第二个下划线实例是 requireJS 加载的实例。
Basically, a library has to be loaded via requireJS for requireJS to have knowledge of it. However, the nexttime you require underscore, requireJS will go "hey, I already loaded that, so just hand back whatever the exports
value is and don't worry about loading another script."
基本上,一个库必须通过 requireJS 加载,requireJS 才能了解它。但是,下次您需要下划线时,requireJS 会“嘿,我已经加载了它,所以只需返回exports
值是什么,不要担心加载另一个脚本。”
This means you have two real options. One is what I would consider an anti-pattern: simply don't use requireJS to express dependencies for global scripts. That is, as long as a library attaches a global to the root context, you'll be able to access it, event if that dependency isn't explicitly required. You can see why this is an anti-pattern - you've basically just eliminated most of the advantages to using an AMD loader (explicit dependency listing and portability).
这意味着您有两个真正的选择。一个是我认为的反模式:根本不使用 requireJS 来表达全局脚本的依赖关系。也就是说,只要库将全局附加到根上下文,您就可以访问它,如果该依赖项不是明确需要的事件。您可以理解为什么这是一种反模式——您基本上已经消除了使用 AMD 加载程序的大部分优势(显式依赖项列表和可移植性)。
The other, better option is using requireJS to load everything, to the degree that the only actual script tag you should create yourself is the one that initially loads requireJS. You can use shims, but 95% of the time it's really not that difficult to add an AMD wrapper to the script instead. It might take a little more work to convert all of your non-AMD libraries to be AMD compatible, but once you've done one or two it gets a lot easier - I can take any generic jQuery plugin and convert it to an AMD module in less than a minute. It's usually just a matter of adding
另一个更好的选择是使用 requireJS 加载所有内容,在某种程度上,您应该自己创建的唯一实际脚本标记是最初加载 requireJS 的那个。您可以使用垫片,但在 95% 的情况下,将 AMD 包装器添加到脚本中并没有那么困难。将所有非 AMD 库转换为与 AMD 兼容可能需要做更多的工作,但是一旦完成一两个库就会变得容易得多 - 我可以使用任何通用的 jQuery 插件并将其转换为 AMD 模块不到一分钟。这通常只是添加的问题
define(['jquery'], function (jQuery) {
at the top, and
在顶部,和
return jQuery;
});
at the bottom. The reason I have 'jquery' mapping to jQuery
rather than $
is that I've noticed most plugins these days are wrapped in a closure like this:
在底部。我将 'jquery' 映射到jQuery
而不是的原因$
是我注意到现在大多数插件都包含在这样的闭包中:
(function ($) {
// plugin code here
})(jQuery);
And it's a good idea to pay attention to the intended scope. You can certainly map 'jquery' to $
directly though, assuming the plugin isn't expecting to find jQuery
instead of $
. That's just the basic AMD wrapper - more complex ones generally try to detect what kind of loader is being used (commonJS vs AMD vs regular ol' globals) and use a different loading method depending on the result. You can find examples of this pretty easily with a few seconds on google.
关注预期范围是个好主意。你当然可以$
直接将 'jquery' 映射到,假设插件不期望 findjQuery
而不是$
. 这只是基本的 AMD 包装器 - 更复杂的包装器通常会尝试检测正在使用的加载器类型(commonJS vs AMD vs 常规 ol' globals)并根据结果使用不同的加载方法。你可以在谷歌上用几秒钟很容易地找到这样的例子。
Update: The workaround I used to support using jQuery.noConflict(true)
with RequireJS worked, but it required a very small modification to the jQuery source, and I have since figured out a much better way to accomplish the same thing without modifying jQuery. Luckily enough, so has James Burke, the author of RequireJS, who has added it to the RequireJS documentation: http://requirejs.org/docs/jquery.html#noconflictmap
更新:我曾经支持使用jQuery.noConflict(true)
RequireJS的解决方法有效,但它需要对 jQuery 源代码进行非常小的修改,而且我已经找到了一种更好的方法来完成相同的事情而无需修改 jQuery。幸运的是,RequireJS 的作者 James Burke 也将其添加到了 RequireJS 文档中:http: //requirejs.org/docs/jquery.html#noconflictmap