Wie verwende ich requireJS und jQuery zusammen?

82

Ich möchte requireJS verwenden und verwende jQuery. Ich möchte die kombinierte Version von requireJS und jQuery nicht verwenden, da ich nicht die neueste jQuery-Version verwende. Was ist der beste Weg für mich, mit requireJS zu arbeiten?

Naor
quelle
2
Was hindert Sie daran, die neueste jQuery zu verwenden?
Inkognito
Ich verwende jQuery 1.3.8. Diese Version funktioniert etwas anders als 1.4.X. Ich bestelle, um die neueste jQuery zu verwenden. Ich muss Code aktualisieren und habe momentan keine Zeit dafür. Darüber hinaus halte ich das Kombinieren von Paketen nicht für richtig.
Naor
Die Antwort unten ist gut ... warum haben Sie sie nicht als richtig markiert?
Gefangener NULL
@Prisoner ZERO: Ehrlich gesagt habe ich es nicht geschafft, es zu testen. Ich habe den Ajax Script Loader von Microsoft verwendet. Vielen Dank, dass Sie mich daran erinnert haben, diese Antwort zu markieren. Wenn Sie sagten, es ist großartig - ich glaube Ihnen.
Naor
Ich fand es auch schwierig, Anforderungen mit anderen Bibliotheken zu verwenden und umgekehrt. Deshalb habe ich eine Bibliothek erstellt, die viel einfacher zu bedienen ist und mit Winkel getestet wird. Unten befindet sich eine Demo-Anwendung: gngeorgiev.github.io/Modulerr.js Sie können auch alle Skripte zu einem kombinieren, ohne von Modulerr.js abhängig zu sein
Georgi-it

Antworten:

130

Das ist auch meine genaue Frage! Ich muss auch eine ältere jQuery verwenden, aber auch "traditionellere" Javascript-Bibliotheken. Was ist die beste Technik, um das zu tun? (Ich kann Ihre Frage bearbeiten, um sie breiter zu gestalten, wenn Sie nichts dagegen haben.) Folgendes habe ich gelernt.

Der RequireJS-Autor James Burke erläuterte die Vorteile der kombinierten RequireJS + jQuery-Datei . Sie bekommen zwei Dinge.

  1. Ein Modul jqueryist verfügbar und es ist das jQuery-Objekt. Das ist sicher:

    // My module depends on jQuery but what if $ was overwritten?
    define(["jquery"], function($) {
      // $ is guaranteed to be jQuery now */
    })
  2. jQuery ist bereits vor jedem geladen require()oder define()Sachen. Allen Modulen wird garantiert, dass jQuery bereit ist. Sie brauchen nicht einmal das require/order.jsPlugin, da jQuery grundsätzlich hartcodiert war, um zuerst geladen zu werden.

Für mich ist # 2 nicht sehr hilfreich. Die meisten realen Anwendungen haben viele .js Dateien, die in der richtigen Reihenfolge geladen werden müssen - traurig, aber wahr. Sobald Sie Sammy oder Underscore.js benötigen, hilft die kombinierte RequireJS + jQuery-Datei nicht mehr.

Meine Lösung besteht darin, einfache RequireJS-Wrapper zu schreiben, die meine traditionellen Skripte mit dem Plugin "order" laden.

Beispiel

Angenommen, meine App verfügt über diese Komponenten (abhängig).

  • Meine App, greatapp
    • greatapp hängt von einer benutzerdefinierten Abfrage ab (alte Version muss ich verwenden)
    • greatapp hängt von my_sammy ab (SammyJS plus alle Plugins, die ich verwenden muss). Diese müssen in Ordnung sein
      1. my_sammy hängt von jquery ab (SammyJS ist ein jQuery-Plugin)
      2. my_sammy hängt von sammy.js ab
      3. my_sammy hängt von sammy.json.js ab
      4. my_sammy hängt von sammy.storage.js ab
      5. my_sammy hängt von sammy.mustache.js ab

In meinen Augen ist alles darüber .jsein "traditionelles" Skript. Alles ohne .jsist ein RequireJS-Plugin. Der Schlüssel ist: High-Level-Inhalte (greatapp, my_sammy) sind Module, und auf tieferen Ebenen wird auf herkömmliche .jsDateien zurückgegriffen.

Booten

Alles beginnt mit einem Booter, der RequireJS sagt, wie er anfangen soll.

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

In habe js/boot.jsich nur die Konfiguration und wie man die Anwendung startet.

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(); }
       );

Hauptanwendung

In habe greatapp.jsich ein normal aussehendes Modul.

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-Modul-Wrapper für herkömmliche Dateien

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;
         }
      );
JasonSmith
quelle
5
Gute Arbeit an dieser Antwort ... hoffentlich wird der Fragesteller dies markieren!
Gefangener
Sie erstellen ein require_jquery-Modul in Abhängigkeit von der tatsächlichen jquery-Datei, aber bleibt jquery nicht global? Ist die eigentliche JQuery-Datei 1.4.2, die Sie aus dem benutzerdefinierten Pfad laden, kein require.js-Modul? oder haben Sie diese Datei auch umwickelt?
Sander
3
Ich möchte darauf hinweisen, dass mit der neuesten Version von jQuery (1.7) bereits Module unterstützt werden. Sie müssen es also nur wie gewohnt benötigen und es wird funktionieren.
Mike Murko
1
Kann jemand diese Antwort aktualisieren, um zu reflektieren, wie JS 2 (mit Shim) + jQuery 1.7+, das AMD unterstützt, erforderlich ist?
Henry
1
Ich möchte nur darauf hinweisen, dass der beste Weg, um Abhängigkeitsunterstützung mit Nicht-AMD-Javascript-Dateien zu erreichen, jetzt die Shim-Konfiguration in RequireJS 2.0+ ist. Wenn Sie Require 1.x weiterhin verwenden, können Sie den Vorläufer des Shims verwenden , wrapjs
Johann
32

Diese Frage ist jetzt mindestens zwei Jahre alt, aber ich habe festgestellt, dass dies immer noch ein Problem mit RequireJS 2.0 ist (require-jquery.js verwendet jQuery 1.8.0, aber die neueste Version ist 1.8.2).

Wenn Sie diese Frage sehen, beachten Sie, dass require-jquery.js jetzt nur require.js und jquery.js sind, die zusammengefügt wurden. Sie können einfach require-jquery.js bearbeiten und die jQuery-Teile durch eine neuere Version ersetzen .

Update (30. Mai 2013) : Nachdem RequireJS über Pfade und Shim verfügt, gibt es eine neue Möglichkeit, jQuery- und jQuery-Plugins zu importieren, und die alte Methode ist weder erforderlich noch empfohlen . Hier ist eine gekürzte Version der aktuellen Methode:

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

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

Weitere Informationen finden Sie unter http://requirejs.org/docs/jquery.html .

Chris
quelle
Danke, das ist immer noch relevant :)
Naor
Verwenden Sie noch jQuery 1.3.8? :)
Chris
Vielen Dank für den Hinweis. Ich habe gelesen, dass eine ältere Version von 1.8 Probleme hatte, die in der neuesten Version gelöst wurden.
Blaine Mucklow
Tatsächlich. Sie waren keine jQueryUI-Probleme mit curCSS, oder?
Chris
1
@AHMED: Beide arbeiten. Der Unterschied besteht darin, dass define explizit ein Modul angibt. Ich bevorzuge die Verwendung von define fast ausschließlich, da es sich um explizitere Pfade handelt: stackoverflow.com/questions/16087635/…
Chris
9

Ich habe festgestellt, dass der beste Ansatz darin besteht, jQuery außerhalb meines RequireJS-Builds zu halten.

Fügen Sie einfach jquery.min.js in Ihr HTML ein. Dann erstellen Sie eine jquery.js-Datei mit so etwas ...

define([], function() {
    return window.$;
});
tkdave
quelle
Dies ist eine gute Problemumgehung, da ich einige JS-Dateien habe, die traditionell mithilfe von Skript-Tags geladen werden müssen und von jQuery abhängen.
Jingtao
3

Ich fand JasonSmiths Antwort enorm hilfreich, wahrscheinlich mehr als die Dokumentation von RequireJS.

Es gibt jedoch eine Möglichkeit zur Optimierung, um zu vermeiden, dass separate AJAX-Anforderungen für (winzige) Module zum Definieren von Definitionen ("require_jquery" "require_sammy") vorliegen. Ich würde vermuten, dass r.js dies in der Optimierungsphase tun würde, aber Sie können dies im Voraus tun, um nicht mit Path, BaseURI-System zu kämpfen.

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

Schließlich myapplication.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
    }
})

Diese Struktur (ersetzt lose (Duplikate?) Das Order Plugin von RequireJS, aber) ermöglicht es Ihnen, die Anzahl der Dateien, die Sie für AJAX benötigen, zu reduzieren und der Definition von abhängigen und abhängigen Bäumen mehr Kontrolle zu verleihen.

Es gibt auch einen großen Vorteil, jQuery separat zu laden (normalerweise 100.000). Sie können das Caching auf dem Server steuern oder jQuery im localStorage des Browsers zwischenspeichern. Schauen Sie sich das AMD-Cache-Projekt hier an: https://github.com/jensarps/AMD-cache. Ändern Sie dann die Definition (Anweisungen, die "Cache!" Einschließen:) und sie bleibt (für immer :) im Browser des Benutzers hängen.

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 
    }
)

Hinweis zu jQuery 1.7.x + Es hängt sich nicht mehr an das Fensterobjekt an, daher funktioniert das oben Gesagte NICHT mit der unveränderten jQuery 1.7.x + -Datei. Dort müssen Sie Ihre jquery **. Js anpassen, um dies vor dem Schließen von "}) (window);" einzuschließen:

;window.jQuery=window.$=jQuery

Wenn in der Konsole "jQuery undefined" -Fehler vorliegen, handelt es sich um ein Zeichen, das die von Ihnen verwendete jQuery-Version nicht an das Fenster anfügt.

Codelizenz: Public Domain.

Angaben: JavaScript oben riecht nach "Pseudocode", da es sich um eine Paraphrasierung (manuelles Beschneiden) von viel detaillierterem Produktionscode handelt. Es ist nicht garantiert, dass der oben dargestellte Code funktioniert, und er wurde NICHT getestet, um wie dargestellt zu funktionieren. Audit, teste es. Semikolons wurden absichtlich weggelassen, da sie gemäß JS-Spezifikation nicht erforderlich sind und Code ohne sie besser aussieht.

ddotsenko
quelle
Nach dem Kampf mit RequireJS (Dinge, die nicht in der richtigen Reihenfolge geladen werden, die Definition nicht respektieren, erfordern Verschachtelung. Andere magische Fehlverhalten) wechselten zu Curl.JS und begannen nachts gut zu schlafen. Es ist nicht so hochgespielt, aber verdammt, es ist stabil und einfach zu handhaben!
Ddotsenko
1

Zusätzlich zur Antwort von jhs finden Sie die neueren Anweisungen auf der Github-Seite require-jquery in der Datei README.md. Es behandelt sowohl den einfachsten Ansatz der Verwendung einer kombinierten Datei jquery / require.js als auch die Verwendung einer separaten Datei jquery.js.

Paul Beusterien
quelle