¿Cómo uso requireJS y jQuery juntos?

82

Me gustaría usar requireJS y estoy usando jQuery. No quiero usar la versión combinada de requireJS y jQuery ya que no estoy usando la última versión de jQuery. ¿Cuál es la mejor manera de trabajar con requireJS?

Naor
fuente
2
¿Qué le impide utilizar la última versión de jQuery?
Incógnito
Estoy usando jQuery 1.3.8. Esta versión funciona un poco diferente a la 1.4.X. Ordeno usar el jQuery más nuevo, tengo que actualizar un código y ahora mismo no tengo tiempo para eso. Además, no creo que combinar paquetes sea lo correcto.
Naor
La respuesta a continuación es buena ... ¿por qué no la ha marcado como correcta?
Prisionero CERO
@Prisoner ZERO: Honestamente, no he logrado probarlo. Recientemente utilicé el cargador de scripts ajax de microsoft. Gracias por recordarme marcar esta respuesta. Si dijiste que es genial, te creo.
Naor
También encontré requirejs difícil de usar con otras bibliotecas y viceversa. Es por eso que creé una biblioteca que es mucho más fácil de usar y se prueba con angular. Hay una aplicación de demostración en la parte inferior: gngeorgiev.github.io/Modulerr.js También puede combinar todos los scripts en uno sin la dependencia de Modulerr.js
Georgi-it

Respuestas:

130

¡Esa es mi pregunta exacta también! También debo usar un jQuery más antiguo, pero también bibliotecas javascript más "tradicionales". ¿Cuál es la mejor técnica para hacer eso? (Si no le importa, puedo editar su pregunta para hacerla más amplia). Esto es lo que aprendí.

El autor de RequireJS, James Burke, explicó las ventajas del archivo combinado RequireJS + jQuery . Obtienes dos cosas.

  1. Hay un módulo,, jquerydisponible y es el objeto jQuery. Esto es seguro:

    // My module depends on jQuery but what if $ was overwritten?
    define(["jquery"], function($) {
      // $ is guaranteed to be jQuery now */
    })
    
  2. jQuery ya está cargado antes que cualquiera require()o define()cosas. Todos los módulos tienen la garantía de que jQuery está listo. Ni siquiera necesita el require/order.jscomplemento ya que jQuery estaba básicamente codificado para cargar primero.

Para mí, el número 2 no es muy útil. La mayoría de las aplicaciones reales tienen muchos .js archivos que deben cargarse en el orden correcto, triste pero cierto. Tan pronto como necesite Sammy o Underscore.js, el archivo combinado RequireJS + jQuery no ayuda.

Mi solución es escribir envoltorios RequireJS simples que carguen mis scripts tradicionales usando el complemento "order".

Ejemplo

Supongamos que mi aplicación tiene estos componentes (por dependencia).

  • Mi aplicación, gran aplicación
    • Greatapp depende de un jquery personalizado (debo usar la versión anterior)
    • Greatapp depende de my_sammy (SammyJS más todos sus complementos que debo usar). Estos deben estar en orden
      1. my_sammy depende de jquery (SammyJS es un complemento de jQuery)
      2. my_sammy depende de sammy.js
      3. my_sammy depende de sammy.json.js
      4. my_sammy depende de sammy.storage.js
      5. my_sammy depende de sammy.mustache.js

En mi opinión, todo lo anterior que termina con .jses un guión "tradicional". Todo lo que no tiene .jses un complemento RequireJS. La clave es: las cosas de alto nivel (Greatapp, my_sammy) son módulos y, en niveles más profundos, recurren a los .jsarchivos tradicionales .

Arranque

Todo comienza con un booter que le dice a RequireJS cómo comenzar.

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

En js/boot.jspuse solo la configuración y cómo iniciar la aplicación.

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

Aplicación principal

En greatapp.jstengo un módulo de aspecto normal.

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

Envoltorios de módulos RequireJS alrededor de archivos tradicionales

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
fuente
5
Buen trabajo en esta respuesta ... ¡ojalá el interrogador lo marque!
Prisionero CERO
creas un módulo require_jquery dependiendo del archivo jquery real, pero ¿jquery no permanece global? el archivo jquery 1.4.2 real que carga desde la ruta personalizada, ¿no es un módulo require.js? ¿O también envolvió require alrededor de ese archivo?
Sander
3
Me gustaría señalar que con la versión más reciente de jQuery (1.7) ya es compatible con módulos, por lo que todo lo que necesita hacer es solicitarlo como de costumbre y funcionará.
MikeMurko
1
¿Alguien puede actualizar esta respuesta para reflejar cómo hacer requireJS 2 (w / shim) + jQuery 1.7+ que sea compatible con AMD?
Henry
1
Solo me gustaría señalar que la mejor manera de lograr el soporte de dependencia con archivos javascript que no son de AMD es ahora la configuración de shim que se encuentra en RequireJS 2.0+. Si todavía está usando Require 1.x, puede usar el precursor de shim. , wrapjs
Johann
32

Esta pregunta tiene al menos dos años ahora, pero noté que este es un problema aún con RequireJS 2.0 (require-jquery.js usa jQuery 1.8.0, pero la última versión es 1.8.2).

Si ve esta pregunta, tenga en cuenta que require-jquery.js ahora solo es require.js y jquery.js, combinados. Puede editar require-jquery.js y reemplazar las partes de jQuery con una versión más nueva .

Actualización (30 de mayo de 2013) : ahora que RequireJS tiene rutas y shim, hay una nueva forma de importar los complementos jQuery y jQuery, y el método anterior ya no es necesario ni recomendado . Aquí hay una versión abreviada del método actual:

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

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

Consulte http://requirejs.org/docs/jquery.html para obtener más información.

Chris
fuente
Gracias, esto sigue siendo relevante :)
Naor
¿Sigues usando jQuery 1.3.8? :)
Chris
Gracias por señalar esto. Leí que una versión anterior de 1.8 tenía problemas que se resolvieron en la última versión.
Blaine Mucklow
En efecto. No eran problemas de jQueryUI con curCSS, ¿verdad?
Chris
1
@AHMED: Ambos funcionan. La diferencia es que define especifica explícitamente un módulo. Tiendo a preferir usar define casi exclusivamente porque es más explícito sobre las rutas: stackoverflow.com/questions/16087635/…
Chris
9

Descubrí que el mejor enfoque es mantener jQuery fuera de mi compilación de RequireJS.

Solo incluye jquery.min.js en tu HTML. Luego, crea un archivo jquery.js con algo como esto ...

define([], function() {
    return window.$;
});
tkdave
fuente
Esta es una buena solución, ya que tengo algunos archivos JS que deben cargarse tradicionalmente usando etiquetas de script, y dependen de jQuery.
jingtao
3

Encontré la respuesta de JasonSmith tremendamente útil, probablemente más que la documentación de RequireJS.

Sin embargo, hay una manera de optimizarlo para evitar tener solicitudes AJAX separadas para (pequeños) módulos de declaración de definición ("require_jquery" "require_sammy"). Sospecho que r.js lo haría en la etapa de optimización, pero puede hacerlo con anticipación para no pelear con Path, el sistema 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

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

Esta estructura (reemplaza vagamente (¿duplica?) El complemento Order de RequireJS, pero) le permite podar la cantidad de archivos que necesita para AJAX, agregando más control a la definición de depende y árbol dependiente.

También hay una gran ventaja al cargar jQuery por separado (que generalmente cuesta 100k): puede controlar el almacenamiento en caché en el servidor o almacenar jQuery en el almacenamiento local del navegador. Eche un vistazo al proyecto AMD-Cache aquí https://github.com/jensarps/AMD-cache y luego cambie las declaraciones define (para incluir "caché!": Y estará (para siempre :)) atascado en el navegador del usuario.

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

Nota sobre jQuery 1.7.x + Ya no se adjunta al objeto de la ventana, por lo que lo anterior NO funcionará con el archivo jQuery 1.7.x + sin modificar. Allí debe personalizar su jquery **. Js para incluir esto antes del cierre "}) (ventana);":

;window.jQuery=window.$=jQuery

Si tiene errores "jQuery undefined" en la consola, es una señal de que la versión de jQuery que está utilizando no se está adjuntando a la ventana.

Licencia de código: dominio público.

Divulgaciones: JavaScript arriba huele a "pseudocódigo" ya que es una paráfrasis (poda manual) de un código de producción mucho más detallado. No se garantiza que el código presentado anteriormente funcione y NO se probó para que funcione como se presenta. Auditoría, pruébalo. Se omiten los puntos y comas a propósito, ya que no son necesarios según las especificaciones de JS y el código se ve mejor sin ellos.

ddotsenko
fuente
Después de luchar con RequireJS (las cosas se cargan fuera de orden, sin respetar la definición, requieren anidamiento. Otras malas conductas mágicas) cambió a Curl.JS y comenzó a dormir bien por la noche. No es tan publicitado, pero, maldita sea, ¡es estable y fácil de trabajar!
ddotsenko
1

Además de la respuesta de jhs, consulte las instrucciones más recientes en la página de github require-jquery del archivo README.md. Cubre tanto el enfoque más simple de usar un archivo jquery / require.js combinado como también cómo usar un jquery.js separado.

Paul Beusterien
fuente