Require.js伤害了我的大脑.关于它加载脚本/模块的方式的一些基本问题

J. *_*rsh 45 javascript jquery module requirejs

我们假设这是我的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();
    });
});
Run Code Online (Sandbox Code Playgroud)
  1. 如果我理解正确,paths配置选项允许您引用脚本,<script>在HTML中引用标签.假设是这种情况,我是否仍然需要在下面的实际require语句中使用a $或下划线对jQuery等脚本进行别名_?考虑到如果您使用标准<script>标记引用jQuery ,$可以在整个脚本中自动使用,这似乎很奇怪.使用它不应该是一样的paths吗?

  2. 我是shim配置选项的新手,据我所知已取代已弃用的order!插件.该exports物业实际上做了什么?它似乎没有为脚本创建别名; 例如,如果我将exports下划线设置为"whatever",然后尝试console.log(whatever),则它是未定义的.那有什么意义呢?

  3. 像jQuery这样的脚本如何"全局"正确使用?也就是说,能够$在我的App.js模块或我的"app"文件夹中的任何其他模块中使用别名的正确方法是什么?我是否必须在每个单独的模块中使用jQuery并且每次都使用别名$?或者我在这里以正确的方式完成它的方式?

我也非常感谢对这个特定剧本的任何其他批评; 在我看来,Require.js的文档还有很多不足之处; 我真的很想知道的事情似乎被掩盖了,让我挠头.

Nic*_*tti 22

  1. 路径告诉require.js何时需要依赖关系.

    例如,我有这样的配置:

    "paths": { 
        "jquery": "require_jquery"
    },
    "shim": {
        "jquery-cookie"  : ["jquery"],
        "bootstrap-tab"  : ["jquery"],
        "bootstrap-modal": ["jquery"],
        "bootstrap-alert": ["jquery"]
    },
    
    Run Code Online (Sandbox Code Playgroud)

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

    define( ['jquery']
    
    Run Code Online (Sandbox Code Playgroud)

    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 );
    } );
    
    Run Code Online (Sandbox Code Playgroud)

    这意味着jQuery只会在我的模块中定义.(这是因为我写了Wordpress插件,因此我可以包含我自己的jQuery版本,而无需触及外部版本)

  2. 导出(从文档读取只是应该是您正在使用的模块的名称,以便在加载正确时可以检测到.这里有解释.所以如果你想为下划线设置导出它应该是_

  3. 正如我所解释的那样,jQuery应该是全局的,如果只是导入它,则执行文件并且jQuery是全局的

编辑 - 回答评论.

  1. 是的我的意思是,你必须为jQuery导出$或jQuery,为主干导出_.根据我从文档中获得的内容,仅在某些边缘情况下才需要这样,并且对于将全局名称空间声明为jQuery的库而言,这不是必需的.

    我认为,当必须从CDN加载jQuery时,需要它们才需要它们.我认为requirejs首先尝试从CDN加载jQuery,然后通过检查"已导出"变量是否存在来进行检查以验证它是否已正确加载,如果不存在,则从本地文件系统加载它(如果你当然已经配置了后备).当requirejs无法看到404回来时,这是需要的.

  2. 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
    } );
    
    Run Code Online (Sandbox Code Playgroud)

  • @J.Ky Marsh:我和你在一起.RequireJS文档虽然很大,但对我帮助不大. (4认同)

Iso*_*ous 22

为了清除任何混淆exports,假设任何填充库将属性附加到全局上下文(windowroot),或修改已存在的全局属性(例如jQuery插件).当requireJS获取加载shimmed依赖项的命令时,它会检查与exports该shim config 的值匹配的属性的全局上下文,如果找到它,则将其作为该模块的值返回.如果找不到它,则它加载相关的脚本,等待它执行,然后找到全局符号并返回它.

需要记住的一个重要事实是,除非shim配置包含exports值,init否则不会执行该配置上的任何方法.依赖性加载器必须exports在初始化该模块之前找到模块的值(这是指定的值),这就是为什么在init该模块存在垫片时需要该属性的原因.

更新:我还需要指出,如果有问题的模块在define任何地方调用,那么您对该模块的任何shim配置都将被忽略.这实际上让我有些头疼,因为我想使用shim配置调用jQuery的jQuery.noConflict(true)方法来取消jQuery的全局化,并将其范围限定为只需要它的模块,但无法让它运行起来.(有关如何使用map config而不是shim config轻松完成此操作的信息,请参阅底部更新.)

更新2:关于requireJS谷歌小组的最近一个问题让我意识到我的解释可能有些误导,所以我想澄清一下.如果通过requireJS加载至少一次, RequireJS将仅重新使用填充的依赖项.也就是说,如果你只是<script>在主机页面上有一个标签(例如,下划线),就像这样:

<script src='lib/underscore.js'></script>
<script src='lib/require.js' data-main='main.js'></script>
Run Code Online (Sandbox Code Playgroud)

...你在requireJS配置中有这样的东西:

paths: {
    'underscore': 'lib/underscore'
},
shim: {
    'underscore': {
        exports: '_'
    }
}
Run Code Online (Sandbox Code Playgroud)

然后第一次执行define(['underscore'], function (_) {});或者var _ = require('underscore');,RequireJS将重新加载下划线库,而不是重新使用先前定义的window._,因为只要requireJS知道,您之前从未加载下划线.当然,它可以检查是否_已经在根作用域上定义,但它无法验证_已经存在的那个与您的paths配置中定义的那个相同.例如,无论是prototypejquery分配自己window.$默认的,如果requireJS假设"窗口.$"是jQuery的时候它实际上是原型,你要在一个糟糕的情况.

所有这一切意味着,如果您混合并匹配脚本加载样式,那么您的页面最终会出现以下情况:

 <script src='lib/underscore.js'></script>
 <script src='lib/require.js' data-main='main.js'></script>
 <script src='lib/underscore.js'></script>
Run Code Online (Sandbox Code Playgroud)

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

基本上,必须通过requireJS加载库,以便requireJS了解它.但是,下次你需要下划线时,requireJS会"嘿,我已经加载了它,所以只需交回任何exports值,不要担心加载另一个脚本."

这意味着您有两个真正的选择.一个是我认为是反模式的:只是不要使用requireJS来表示全局脚本的依赖关系.也就是说,只要库将全局附加到根上下文,您就能够访问它,如果没有明确要求该依赖项,则会发生事件.你可以看到为什么这是一个反模式 - 你基本上只是消除了使用AMD加载器的大部分优势(显式依赖列表和可移植性).

另一个更好的选择是使用requireJS加载所有内容,以至于您自己应该创建的唯一实际脚本标记是最初加载requireJS的标记.您可以使用填充程序,但95%的情况下,在脚本中添加AMD包装器并不困难.将所有非AMD库转换为AMD兼容可能需要更多的工作,但是一旦完成了一两个,它就会变得容易多了 - 我可以使用任何通用的jQuery插件并将其转换为AMD模块不到一分钟 这通常只是一个问题

define(['jquery'], function (jQuery) {
Run Code Online (Sandbox Code Playgroud)

在顶部,和

    return jQuery;
});
Run Code Online (Sandbox Code Playgroud)

在底部.我有'jquery'映射的原因jQuery而不是$我注意到这些天大多数插件都包含在这样的闭包中:

(function ($) {
    // plugin code here
})(jQuery);
Run Code Online (Sandbox Code Playgroud)

注意预期的范围是个好主意.你当然可以$直接映射'jquery' ,假设插件不希望找到jQuery而不是$.这只是基本的AMD包装器 - 更复杂的通常会尝试检测正在使用哪种加载器(commonJS vs AMD与常规ol'全局)并根据结果使用不同的加载方法.你可以在google上用几秒钟轻松找到这个例子.

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

  • 这可能是RequireJS垫片使用的唯一最有用的解释之一.'exports:'属性经常被混淆为指示RequireJS*应该*附加脚本的位置,而实际上它是*它看起来*的地方,作为它已准备好的指示.谢谢! (2认同)