Как использовать вместе requireJS и jQuery?

82

Я хотел бы использовать requireJS, и я использую jQuery. Я не хочу использовать комбинированную версию requireJS и jQuery, так как я не использую последнюю версию jQuery. Как мне лучше всего работать с requireJS?

Наор
источник
2
Что мешает вам использовать последнюю версию jQuery?
Incognito,
Я использую jQuery 1.3.8. Эта версия работает немного иначе, чем 1.4.X. Я приказываю использовать новейший jQuery. Мне нужно обновить код, и сейчас у меня нет на это времени. Более того, я не думаю, что объединение пакетов - это правильно.
Naor
Ответ ниже хорош ... почему вы не отметили его как правильный?
Prisoner ZERO
@Prisoner ZERO: Честно говоря, мне не удалось это проверить. Недавно я использовал загрузчик скриптов ajax от Microsoft. Спасибо, что напомнили мне отметить этот ответ. Если вы сказали, что это здорово - я вам верю.
Наор
Я также обнаружил, что requirejs трудно использовать с другими библиотеками, и наоборот. Вот почему я создал библиотеку, которая намного проще в использовании и протестирована с angular. Внизу есть демонстрационное приложение: gngeorgiev.github.io/Modulerr.js. Вы также можете объединить все скрипты в один без зависимости от Modulerr.js
Георги-ит

Ответы:

130

Это и мой точный вопрос! Я также должен использовать более старый jQuery, но также и более «традиционные» библиотеки javascript. Какой метод лучше всего сделать? (Я могу отредактировать ваш вопрос, чтобы сделать его более широким, если вы не возражаете.) Вот что я узнал.

Автор RequireJS Джеймс Берк объяснил преимущества объединенного файла RequireJS + jQuery . Вы получите две вещи.

  1. jqueryДоступен модуль ,, и это объект jQuery. Это безопасно:

    // My module depends on jQuery but what if $ was overwritten?
    define(["jquery"], function($) {
      // $ is guaranteed to be jQuery now */
    })
    
  2. jQuery уже загружен перед любым require()или define()другим. Для всех модулей гарантирована готовность jQuery. Вам даже не нужен require/order.jsплагин, поскольку jQuery был жестко запрограммирован для загрузки первым.

Для меня №2 не очень помогает. В большинстве реальных приложений есть много .js файлов, которые необходимо загружать в правильном порядке - печально, но факт. Как только вам понадобится Sammy или Underscore.js, объединенный файл RequireJS + jQuery не поможет.

Мое решение - написать простые обертки RequireJS, которые загружают мои традиционные сценарии с помощью плагина «порядок».

пример

Предположим, в моем приложении есть эти компоненты (по зависимости).

  • Мое приложение, greatapp
    • greatapp зависит от пользовательского jquery (я должен использовать старую версию)
    • greatapp зависит от my_sammy (SammyJS плюс все его плагины, которые я должен использовать). Они должны быть в порядке
      1. my_sammy зависит от jquery (SammyJS - это плагин jQuery)
      2. my_sammy зависит от sammy.js
      3. my_sammy зависит от sammy.json.js
      4. my_sammy зависит от sammy.storage.js
      5. 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
вы создаете модуль require_jquery в зависимости от фактического файла jquery, но разве jquery не остается глобальным? фактический файл jquery 1.4.2, который вы загружаете из настраиваемого пути, не является модулем require.js? или вы тоже обернули требуемый файл?
Сандер
3
Я хотел бы отметить, что в самой последней версии jQuery (1.7) он уже поддерживает модули, поэтому все, что вам нужно сделать, это потребовать его как обычно, и он будет работать.
MikeMurko
1
может ли кто-нибудь обновить этот ответ, чтобы отразить, как сделать requireJS 2 (w / shim) + jQuery 1.7+, который поддерживает AMD?
Генри
1
Я просто хотел бы указать, что лучший способ добиться поддержки зависимостей с файлами javascript, отличными от AMD, теперь - это конфигурация прокладки, найденная в RequireJS 2.0+. Если вы все еще используете Require 1.x, вы можете использовать предшественник прокладки , wrapjs
Johann
32

Этому вопросу уже не менее двух лет, но я заметил, что проблема все еще связана с RequireJS 2.0 (require-jquery.js использует jQuery 1.8.0, но последняя версия - 1.8.2).

Если вы случайно столкнулись с этим вопросом, обратите внимание, что require-jquery.js теперь представляет собой просто require.js и jquery.js, смешанные вместе. Вы можете просто отредактировать require-jquery.js и заменить части jQuery более новой версией .

Обновление (30 мая 2013 г.) : теперь, когда RequireJS имеет пути и прокладку, появился новый способ импорта подключаемых модулей 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 для получения дополнительной информации.

Крис
источник
Спасибо, это все еще актуально :)
Naor
Вы все еще используете jQuery 1.3.8? :)
Крис
Спасибо, что указали на это. Я читал, что в более старой версии 1.8 были проблемы, которые были решены в последней.
Blaine Mucklow
Конечно. Это не были проблемы jQueryUI с curCSS, не так ли?
Крис
1
@AHMED: Они оба работают. Разница в том, что define явно указывает модуль. Я предпочитаю использовать определение почти исключительно потому, что в нем более четко указаны пути: stackoverflow.com/questions/16087635/…
Крис,
9

Я нашел лучший подход - оставить jQuery вне моей сборки RequireJS.

Просто включите jquery.min.js в свой HTML. Затем создайте файл jquery.js примерно так ...

define([], function() {
    return window.$;
});
tkdave
источник
Это хороший обходной путь, так как у меня есть несколько файлов JS, которые необходимо загружать традиционно с помощью тегов сценариев, и они зависят от jQuery.
jingtao
3

Находил ответ JasonSmith чрезвычайно полезным, возможно, даже больше, чем документация RequireJS.

Однако есть способ оптимизировать его, чтобы избежать отдельных запросов AJAX для (крошечных) модулей объявления определения ("require_jquery", "require_sammy"). Я подозреваю, что 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

наконец, 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
    }
})

Эта структура (слабо заменяет (дублирует?) Плагин RequireJS Order, но) позволяет сократить количество файлов, которые вам нужны, до AJAX, добавляя больше контроля к определению зависимостей и зависимого дерева.

Также есть большой бонус к загрузке jQuery по отдельности (которая обычно составляет 100 КБ) - вы можете управлять кешированием на сервере или кешировать 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 +. Он больше не прикрепляется к объекту окна, поэтому вышеуказанное НЕ будет работать с немодифицированным файлом jQuery 1.7.x +. Здесь вы должны настроить свой jquery **. Js, чтобы включить это перед закрытием "}) (window);":

;window.jQuery=window.$=jQuery

Если у вас есть ошибка «jQuery undefined» в консоли, это означает, что используемая вами версия jQuery не прикрепляется к окну.

Код лицензии: общественное достояние.

Раскрытие информации: приведенный выше JavaScript пахнет «псевдокодом», поскольку он является перефразированием (сокращением вручную) гораздо более подробного производственного кода. Работа приведенного выше кода не гарантируется, и его работа НЕ проверялась. Проведите аудит, проверьте это. Точки с запятой опущены намеренно, так как они не требуются согласно спецификации JS, и код без них выглядит лучше.

ддоценко
источник
После борьбы с RequireJS (вещи загружаются не по порядку, не соблюдают определения, требуют вложенности. Другие магические нарушения) переключился на Curl.JS и начал спать спокойно по ночам. Он не такой раскрученный, но, блин, стабилен и с ним легко работать!
ддоценко 04
1

В дополнение к ответу jhs см. Более свежие инструкции на странице github require-jquery в файле README.md. Он охватывает как простейший подход к использованию комбинированного файла jquery / require.js, так и то, как использовать отдельный jquery.js.

Поль Beusterien
источник