如何同时使用requireJS和jQuery?

82
我想使用requireJS,同时也在使用jQuery。 但是我不想使用合并过的requireJS和jQuery版本,因为我没有用最新的jQuery版本。 对于我来说,最好的处理requireJS的方式是什么?

2
什么阻止你使用最新的jQuery? - Incognito
我正在使用jQuery 1.3.8版本。这个版本与1.4.X有些不同。为了使用最新的jQuery,我必须更新一些代码,但现在我没有时间进行更新。此外,我认为组合软件包并不是正确的做法。 - Naor
下面的答案很好,为什么你还没有标记它为正确? - Prisoner ZERO
@囚犯ZERO:老实说,我还没有来得及测试。最终我使用了微软的ajax脚本加载器。谢谢你提醒我标记这个答案。如果你说它很棒,我相信你。 - Naor
我也发现 requirejs 在与其他库相互使用时很难使用。这就是为什么我创建了一个更易于使用的库,并使用 angular 进行了测试。底部有一个演示应用程序:gngeorgiev.github.io/Modulerr.js 您还可以将所有脚本合并为一个,而无需依赖 Modulerr.js。 - Georgi-it
5个回答

133
这是我的确切问题!我必须使用旧版jQuery,但也需要更多"传统的"Javascript库。如何做到最好?(如果您不介意,我可能会编辑您的问题以使其更加广泛。)这是我所学到的。
RequireJS的作者James Burke解释了结合RequireJS和jQuery文件的优势。您将获得两个东西。
  1. A module, jquery, is available, and it's the jQuery object. This is safe:

    // My module depends on jQuery but what if $ was overwritten?
    define(["jquery"], function($) {
      // $ is guaranteed to be jQuery now */
    })
    
  2. jQuery is already loaded before any require() or define() stuff. All modules are guaranteed that jQuery is ready. You don't even need the require/order.js plugin since jQuery was basically hard-coded to load first.

对我来说,#2并不是很有用。大多数真实应用程序有许多必须按正确顺序加载的.js文件 - 悲哀但却是事实。一旦需要使用Sammy或Underscore.js,合并了RequireJS + jQuery文件就没有任何帮助。
我的解决方案是编写简单的RequireJS包装器,使用“order”插件加载我的传统脚本。
示例:
假设我的应用程序具有以下组件(按依赖项):
- My app,greatapp - greatapp 依赖于自定义jquery(我必须使用的旧版本) - greatapp 依赖于my_sammy(SammyJS及其所有插件,我必须使用)。这些必须按顺序排列 - my_sammy 依赖于jquery(SammyJS 是一个jQuery插件) - my_sammy 依赖于sammy.js - my_sammy 依赖于sammy.json.js - my_sammy 依赖于sammy.storage.js - my_sammy 依赖于sammy.mustache.js

在我看来,所有以.js结尾的文件都是“传统”的脚本。没有.js的文件都是RequireJS插件。关键在于:高级别的东西(greatapp,my_sammy)是模块,在更深层次上,它回退到传统的.js文件。

引导

一切都始于一个引导程序告诉RequireJS如何启动。

<html>
  <head>
    <script data-main="js/boot.js" src="js/require.js"></script>
  </head>
</html>

js/boot.js文件中,我只放置了配置和如何启动应用程序的内容。
require( // The "paths" maps module names to actual places to fetch the file.
         // I made modules with simple names (jquery, sammy) that will do the hard work.
         { paths: { jquery: "require_jquery"
                  , sammy : "require_sammy"
                  }
         }

         // Next is the root module to run, which depends on everything else.
       , [ "greatapp" ]

         // Finally, start my app in whatever way it uses.
       , function(greatapp) { greatapp.start(); }
       );

主要应用程序

greatapp.js 中,我有一个看起来很普通的模块。

define(["jquery", "sammy"], function($, Sammy) {
  // At this point, jQuery and SammyJS are loaded successfully.
  // By depending on "jquery", the "require_jquery.js" file will run; same for sammy.
  // Those require_* files also pass jQuery and Sammy to here, so no more globals!

  var start = function() {
    $(document).ready(function() {
      $("body").html("Hello world!");
    })
  }

  return {"start":start};
}

将传统文件包装成RequireJS模块

require_jquery.js:

define(["/custom/path/to/my/jquery.js?1.4.2"], function() {
  // Raw jQuery does not return anything, so return it explicitly here.
  return jQuery;
})

require_sammy.js:

// These must be in order, so use the "order!" plugin.
define([ "order!jquery"
       , "order!/path/to/custom/sammy/sammy-0.6.2-min.js"
       , "order!/path/to/custom/sammy/plugins/sammy.json-0.6.2-min.js"
       , "order!/path/to/custom/sammy/plugins/sammy.storage-0.6.2-min.js"
       , "order!/path/to/custom/sammy/plugins/sammy.mustache-0.6.2-min.js"
       ]

       , function($) {
           // Raw sammy does not return anything, so return it explicitly here.
           return $.sammy;
         }
      );

5
做得很好,希望提问者会标记这个答案! - Prisoner ZERO
@JasonSmith,看一下这个链接http://ryanflorence.com/non-trivial-js/,不用在意dustjs/qunit部分,但是关注publisher、requirejs和jquery,你可能会改变你设置应用程序的想法。 - Poelinca Dorin
3
我需要指出的是,最新版本的jQuery(1.7)已经支持模块化,所以您只需要像平常一样要求它并且它就能够工作了。 - MikeMurko
1
有人能否更新这个答案,以反映如何使用支持AMD的requireJS 2(带shim)+ jQuery 1.7+? - Henry
1
我想指出的是,现在实现非AMD JavaScript文件的依赖支持的最佳方法是使用RequireJS 2.0+中找到的shim配置。如果您仍在使用Require 1.x,则可以使用shim的前身wrapjs - Johann
显示剩余3条评论

33

这个问题已经至少有两年的历史了,但我注意到它仍然是RequireJS 2.0的问题(require-jquery.js使用的是jQuery 1.8.0,但最新版本是1.8.2)。

如果你看到了这个问题,请注意:require-jquery.js现在只是将require.js和jquery.js混合在一起您可以编辑require-jquery.js并用更新版本替换其中的jQuery部分

更新(2013年5月30日): 现在,由于RequireJS有了paths和shim,有了一种新的导入jQuery和jQuery插件的方法,旧的方法已不再必要,也不再推荐使用。以下是当前方法的简化版本:

requirejs.config({
    "paths": {
      "jquery": "//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min"
    }
});

define(["jquery"], function($) {
    $(function() {
    });
});

查看http://requirejs.org/docs/jquery.html获取更多信息。


你还在使用jQuery 1.3.8吗? :) - Chris
确实。它们不是curCSS的jQueryUI问题,对吧? - Chris
你的意思是要使用(require(["jquery"], function($)))而不是(define(["jquery"], function($))),对吗? - AHMED EL-HAROUNY
1
@AHMED:它们都可以工作。不同之处在于define明确指定了一个模块。我倾向于几乎完全使用define,因为它更明确地说明了路径:https://dev59.com/73HYa4cB1Zd3GeqPHhyc - Chris
这应该是被接受的答案。非常有帮助,因为简单易懂。 - Fariz Luqman
显示剩余2条评论

9

我认为最好的方法是在RequireJS构建之外使用jQuery。

只需在HTML中包含jquery.min.js。然后,创建一个类似于以下内容的jquery.js文件...

define([], function() {
    return window.$;
});

这是一个很好的解决方法,因为我有一些JS文件必须使用脚本标签传统加载,并且它们依赖于jQuery。 - jingtao

3

发现JasonSmith的答案非常有帮助,可能比RequireJS的文档更好。

然而,有一种方法可以优化它,避免为(微小的)定义声明模块(“require_jquery”“require_sammy”)单独使用AJAX请求。我会怀疑r.js会在优化阶段完成它,但你可以提前处理,以避免与Path、BaseURI系统打交道。

index.html:

<html>
  <head>
    <script data-main="js/loader.js" src="js/require.js"></script>
  </head>
</html>

loader.js:

// We are going to define( dependencies by hand, inline.
// There is one problem with that through (inferred from testing):
// Dependencies are starting to load (and execute) at the point of declaring the inline
// define, not at the point of require(
// So you may want to nest the inline-defines inside require( 
// this is, in a way, short replacement for Order plug in, but allows you to use
// hand-rolled defines, which the Order plug in, apparently does not allow.

var jQueryAndShims = ['jquery']

if(window.JSON == null){
    jQueryAndShims.push('json2')
    define(
        'json2'
        , ['js/libs/json2.min.js']
        , function() {
            return window.JSON
        }
    )
}
// will start loading the second we define it.
define(
    'jquery'
    , ['js/libs/jquery_custom.min.js']
    , function() {
        // we just pick up global jQuery here. 
        // If you want more than one version of jQuery in dom, read a more complicated solution discussed in
        // "Registering jQuery As An Async-compatible Module" chapter of
        // http://addyosmani.com/writing-modular-js/
        return window.jQuery 
    }
)

// all inline defines for resources that don't rely on other resources can go here.

// First level require(
// regardless of depends nesting in 'myapp' they will all start downloading 
// at the point of define( and exec whenever they want, 
// async in many browsers. Actually requiring it before the nested require makes
// sure jquery had *executed and added jQuery to window object* before
// all resolved depends (jquery plugins) start firing.
require(jQueryAndShims, function($) {

    // will start loading the second we define it.        
    define(
        'sammy_and_friends'
        , ['jquery','js/libs/jquery_pluginone.min.js','js/libs/jquery_plugintwo.min.js','js/libs/sammy.min.js']
        , function($) {
            // note, all plugins are unaltered, as they are shipped by developers.
            // in other words, they don't have define(.. inside.
            // since they augment global $ (window.jQuery) anyway, and 'jquery' define above picks it up
            // , we just keep on returning it.
            // Sammy is attached to $ as $.sammy, so returning just Sammy makes little sense
            return $
        }
    )

    // second level require - insures that Sammy (and other jQuery plugins) - 'sammy_and_friends' - is
    // loaded before we load Sammy plugins. I normally i would inline all sammy plugins i need 
    // (none, since i use none of them preferring jQuery's direct templating API
    // and no other Sammy plug in is really of value. )  right into sammy.js file. 
    // But if you want to keep them separate:
    require(['sammy_and_friends'], function() {

        // will start loading the second we define it.
        define(
            'sammy_extended'
            , ['sammy_and_friends','js/libs/sammy_pluginone.min.js','js/libs/sammy_plugintwo.min.js']
            , function($) {
                // as defined above, 'sammy_and_friends' actually returns (globall) jQuery obj to which
                // Sammy is attached.  So we continue to return $
                return $
            }
        )
        // will start loading the second we define it.
        define(
            'myapp'
            , ['sammy_extended', 'js/myapplication_v20111231.js'] 
            , function($, myapp_instantiator) {
                // note, myapplication may, but does not have to contain RequireJS-compatible define
                // that returns something. However, if it contains something like 
                // "$(document).ready(function() { ... " already it MAY fire before 
                // it's depends - 'sammy_extended' is fully loaded.
                // Insdead i recommend that myapplication.js returns a generator 
                // (app-object-generating function pointer)
                // that takes jQuery (with all loaded , applied plugins) 
                // The expectation is that before the below return is executed, 
                // all depends are loaded (in order of depends tree)
                // You would init your app here like so:
                return myapp_instantiator($)
                // then "Run" the instance in require( as shown below
            }
        )

        // Third level require - the one that actually starts our application and relies on
        // dependency pyramid stat starts with jQuery + Shims, followed by jQuery plugins, Sammy, 
        // followed by Sammy's plugins all coming in under 'sammy_extended'
        require(['jquery', 'myapp'], function($, myappinstance) {
            $(document).ready(function() {myappinstance.Run()})
        })
    }) // end of Second-level require
}) // end of First-level require

最后,我的application.js文件:

// this define is a double-wrap.
// it returns application object instantiator that takes in jQuery (when it's available) and , then, that
// instance can be "ran" by pulling .Run() method on it.
define(function() {
    // this function does only two things:
    // 1. defines our application class 
    // 2. inits the class and returns it.
    return function($) {
        // 1. defining the class
        var MyAppClass = function($) {
            var me = this
            this._sammy_application = $.sammy(function() {
                this.raise_errors = true
                this.debug = true
                this.run_interval_every = 300
                this.template_engine = null
                this.element_selector = 'body'
                // ..
            })
            this._sammy_application.route(...) // define your routes ets...
            this.MyAppMethodA = function(blah){log(blah)}  // extend your app with methods if you want
            // ...
             // this one is the one we will .Run from require( in loader.js
            this.Run = function() {
                me._sammy_application.run('#/')
            }
        }
        // 2. returning class's instance
        return new MyAppClass($) // notice that this is INITED app, but not started (by .Run) 
        // .Run will be pulled by calling code when appropriate
    }
})

这个结构(可能替代(重复?)RequireJS的Order plugin,但)允许您修剪需要进行AJAX的文件数,更加精细地控制depends和depend tree的定义。
此外,分开加载jQuery(通常为100k)也有很大的优势-您可以在服务器上控制缓存,或将jQuery缓存到浏览器的localStorage中。请看这里的AMD-Cache项目:https://github.com/jensarps/AMD-cache ,然后将define语句更改为包含“cache!”:它将永久地固定在用户的浏览器中。
define(
    'jquery'
    , ['cache!js/libs/jquery_old.min.js']
    , function() {
        // we just pick up global jQuery here. 
        // If you want more than one version of jQuery in dom, read a more complicated solution discussed in
        // "Registering jQuery As An Async-compatible Module" chapter of
        // http://addyosmani.com/writing-modular-js/
        return window.jQuery 
    }
)

注意jQuery 1.7.x+的变化,它不再附加到window对象上,因此以上代码将无法使用未修改的jQuery 1.7.x+文件。因此,您必须自定义您的jquery**.js,在关闭“})( window );”之前包含以下内容:
;window.jQuery=window.$=jQuery

如果您在控制台中看到“jQuery未定义”错误,则表明您正在使用的jQuery版本未将其自身附加到窗口。
代码许可证:公共领域。
披露: 上面的JavaScript代码有点像“伪代码”,因为它是对更详细的生产代码的概括(手动修剪)。如上所示的代码不能保证有效,并且未经过测试。请进行审核和测试。 分号故意省略,因为它们不符合JS规范要求,而且没有它们的代码看起来更好。

在与RequireJS的斗争中(加载顺序混乱,不遵守定义,需要嵌套等其他魔法行为),我转向了Curl.JS,并开始晚上能够安心睡觉。虽然它没有那么被炒作,但是它真的很稳定且易于使用! - ddotsenko

1
除了jhs的答案外,还可以查看require-jquery github页面上README.md文件中的最新说明。它涵盖了使用组合jquery/require.js文件以及如何使用单独的jquery.js的最简单方法。

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