Require.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(); }); }); 
  1. 如果我理解正确, pathsconfiguration选项允许您引用脚本,a-la HTML内的<script>标记。 假设是这样的话,我是否还需要在下面的实际require语句中使用带有_或下划线的jQuery来替代像jQuery这样的脚本? 看起来很奇怪,因为如果你用一个标准的<script>标签来引用jQuery,那么$可以在整个脚本中自动使用。 不应该使用paths相同吗?

  2. 我是新的shimconfiguration选项,我明白已经取代了弃用的order! 插入。 exports物业实际上做了什么? 它似乎没有为脚本创build一个别名; 例如,如果我将下划线的exports设置为"whatever" ,然后尝试console.log(whatever) ,它是未定义的。 那么有什么意义呢?

  3. 像“全球?”这样的脚本如何正确使用? 也就是说,能够在我的App.js模块中使用$别名,或者在我的“app”文件夹中使用其他模块的正确方法是什么? 我是否需要在每个单独的模块中使用jQuery,并且每次都要使用别名$ ? 还是我在这里以正确的方式做到这一点?

我也非常感谢这个剧本的其他批评。 在我看来,Require.js的文档有很多不足之处, 我真正想知道更多的东西似乎被掩盖了,让我挠脑袋。

  1. path告诉require.js当你需要依赖关系的时候在哪里看。

    例如,我有这样的东西configuration:

     "paths": { "jquery": "require_jquery" }, "shim": { "jquery-cookie" : ["jquery"], "bootstrap-tab" : ["jquery"], "bootstrap-modal": ["jquery"], "bootstrap-alert": ["jquery"] }, 

    这意味着我每次都在一个模块中

     define( ['jquery'] 

    requirejs从主path加载文件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版本,而无需触及外部版本)

  2. 导出(从文档中读取应该只是你正在使用的模块的名字,以便在加载正确的时候能够被检测出来) 这里解释一下,所以如果你想为下划线设置一个导出,

  3. jQuery应该是全局的,正如我所解释的那样,如果你只是简单地导入它,文件就会被执行,jQuery是全局的

编辑 – 回答评论。

  1. 是的,我的意思是说,你必须出口$或jQuery的jQuery和_骨干。 从我从文档中得到的这只是在一些边缘情况下才需要的,对于在全局命名空间中声明自己为jQuery的库是不必要的。

    我认为requirejs需要它们,当它从CDN加载jQuery时需要回退。 我认为requirejs首先尝试从CDN中加载jQuery,然后通过检查“exported”variables是否存在来检查它是否正确加载,如果不存在,它会从本地文件系统加载它(如果你当然configuration了后备)。 这是requirejs无法看到404回来时所需要的东西。

  2. jQuery是全局可用的,因为它声明为全局的。 如果你只是加载和执行jQuery脚本,你将最终得到两个全局variables$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 } ); 

只是为了消除exports困惑,假定任何一个shim库将一个属性附加到全局上下文( windowroot ),或者修改一个已经存在的全局属性(例如一个jQuery插件)。 当requireJS获取命令加载一个shimmed依赖项时,它检查一个匹配该shim config的exports值的属性的全局上下文,如果发现它,则返回它作为该模块的值。 如果没有find它,则加载关联的脚本,等待它执行,然后find全局符号并将其返回。

需要记住的一个重要的事实是,除非shimconfiguration包含一个exports值,否则该configuration的任何init方法将不会被执行。 在模块可以被初始化之前,依赖加载器必须为模块定义一个值(这是exports指定的),这就是为什么如果该模块有一个shim init需要该属性的原因。

更新:我还需要指出的是,如果有问题的模块在任何地方调用,那么您为该模块设置的任何shimconfiguration都将被忽略。 这实际上让我有些头痛,因为我想用shimconfiguration来调用jQuery的jQuery.noConflict(true)方法来解除jQuery的合并,并将其限制在需要它的模块中,但是却无法让它工作。 (有关如何使用mapconfiguration而不是shim config轻松完成此操作的信息,请参阅底部的更新。)

更新2:最近关于requireJS谷歌的一个问题让我意识到,我的解释可能有点误导,所以我想澄清一下。 如果通过requireJS至less加载一次, RequireJS将只能重新使用一个shimmed依赖。 也就是说,如果您只在主页上有一个<script>标记(比如下划线),就像这样:

 <script src='lib/underscore.js'></script> <script src='lib/require.js' data-main='main.js'></script> 

…在你的requireJSconfiguration中你有这样的东西:

 paths: { 'underscore': 'lib/underscore' }, shim: { 'underscore': { exports: '_' } } 

然后第一次define(['underscore'], function (_) {});var _ = require('underscore'); ,RequireJS将重新加载下划线库,而不是重新使用之前定义的window._ ,因为据requireJS知道,你以前从来没有加载下划线。 当然,它可以检查是否已经在根作用域上定义了_ ,但无法validation已经存在的_是否与pathsconfiguration中定义的_相同。 例如, prototypejquery将自己分配给window.$ ,默认情况下,如果requireJS假定'window。$'是jQuery,那么实际上就是原型,那么就会遇到一个不好的情况。

所有这一切意味着如果你像这样混合搭配脚本加载样式,你的页面就会像这样:

  <script src='lib/underscore.js'></script> <script src='lib/require.js' data-main='main.js'></script> <script src='lib/underscore.js'></script> 

第二个下划线实例是由requireJS加载的实例。

基本上,一个库必须通过requireJS加载requireJS来了解它。 然而, 一次你需要下划线时,requireJS将会“嗨,我已经加载了,所以只要交出任何exports值,不要担心加载另一个脚本。

这意味着你有两个真正的select。 一个是我认为是反模式的:不要用requireJS来表示全局脚本的依赖关系。 也就是说,只要一个库连接到一个全局的根上下文,你就可以访问它,如果这个依赖不是明确需要的话。 你可以看到为什么这是一个反模式 – 你基本上已经消除了使用AMD加载器(显式依赖列表和可移植性)的大多数优点。

另一个更好的select是使用requireJS来加载所有的东西,只有你自己创build的唯一的脚本标签是最初加载requireJS的那个。 你可以使用垫片,但95%的时间,而不是很难添加一个AMD包装的脚本。 将所有的非AMD库转换为兼容AMD可能需要做更多的工作,但是一旦你完成了一两个,它就变得更容易了 – 我可以使用任何通用的jQuery插件并将其转换为AMD模块在不到一分钟的时间。 这通常只是一个添加的问题

 define(['jquery'], function (jQuery) { 

在顶部,和

  return jQuery; }); 

在底部。 我把jquery映射到jQuery而不是$是我注意到现在大多数插件被封装在这样的闭包中:

 (function ($) { // plugin code here })(jQuery); 

注意预期范围是个好主意。 你当然可以直接将'jquery'映射到$ ,假设插件不希望findjQuery而不是$ 。 这只是AMD的基本包装 – 更复杂的包装通常试图检测使用什么样的装载器(commonJS vs AMD vs普通ol'全局variables),并根据结果使用不同的加载方法。 你可以在google上几秒钟find这个例子。

更新:我曾经支持在RequireJS中使用jQuery.noConflict(true)的解决方法,但是它需要对jQuery源代码进行非常小的修改,而且我已经想出了一个更好的方法来完成同样的事情,而不用修改jQuery。 幸运的是,RequireJS的作者James Burke已经将它添加到了RequireJS文档中: http : //requirejs.org/docs/jquery.html#noconflictmap