Правильный способ загрузки плагинов jQuery, не совместимых с AMD, в require.js с помощью jQuery в режиме noConflict?

Скажем, я хочу использовать jquery вместе со стандартным подключаемым модулем jquery, не поддерживающим AMD, который был определен с использованием стандартного замыкания: (function($))( $.fn.myplugin = { ... } )(jQuery);, и все это находится внутри js/libs/jquery/jquery.myplugin.js.

Я использую этот конфиг:

require.config({
  baseUrl: 'js/',
  paths: {
    'jquery':          'libs/jquery/jquery-noconflict', 
    'underscore':      'libs/underscore/underscore',
    'backbone':        'libs/backbone/backbone',
    'jquery-myplugin': 'libs/jquery/jquery.myplugin'
  },
  shim: {
    'backbone': {
      deps: ['underscore', 'jquery'],
      exports: 'Backbone'
  },
  'jquery-myplugin': {
    deps:  ['jquery'] 
  }
});

Я загружаю jQuery в режиме отсутствия конфликтов в libs/jquery/jquery-noconflict.js, потому что не хочу загрязнять глобальное пространство имен:

define(['libs/jquery'], function () {
  return jQuery.noConflict(true);
});

и вот как я загружаю свой основной app.js:

define([
  'jquery',
  'underscore',
  'backbone',
  'jquery-myplugin'],
function($, _, Backbone, MyPlugin){
  //MyPlugin is always undefined, not even sure if 
  //I should be passing it here if it only extends jQuery?
});

Теперь вот проблема, с которой я столкнулся: хотя я могу без проблем использовать все библиотеки, определенные выше, я не смог разработать правильную конфигурацию shim для загрузки плагинов jquery без поддержки AMD.

Я пытался настроить jquery-myplugin как deps из jquery (и наоборот), но так и не смог заставить его работать.

Кажется, у меня проблема со следующим сценарием:

  1. jQuery загружается в бесконфликтном режиме.
  2. код плагина запускается, расширяя экземпляр jQuery выше
  3. Я могу использовать $ в своем приложении, расширенном кодом плагина, поэтому доступен $.myplugin.

Я видел похожие вопросы, но ни один из них на самом деле не решает эту проблему, давая только расплывчатые предложения, такие как «использовать конфигурацию прокладки» ...

Изменить

Я также пытался использовать

"jquery-myplugin": {
    deps: ["jquery"],
    exports: "jQuery.fn.myplugin"
}

И хотя методы плагинов доступны после загрузки в качестве модуля AMD таким образом, я все еще не могу получить доступ: $('.class').myplugin() поскольку объект $ по умолчанию не был расширен с помощью кода моего плагина.


person rochal    schedule 07.01.2013    source источник
comment
У вас все еще есть проблемы с этим?   -  person Louis    schedule 27.03.2014
comment
зависимости модуля должны быть выражены в виде массива: define(['dep1', 'dep2'...'depn'], function(dep1, dep2, ...depn){}); как описано здесь   -  person Vikram    schedule 19.08.2014
comment
хороший улов, но это была просто моя опечатка в stackoverflow, основная проблема все еще существует   -  person rochal    schedule 21.08.2014


Ответы (3)


Использование jQuery.noConflict(true) удаляет глобальную переменную jQuery. Когда ваш подключаемый модуль загружается, он пытается получить доступ к jQuery, но не может, что приводит к сбою.

Если бы ваш плагин был модулем, он мог бы получить доступ к jQuery в качестве зависимости. Или вы можете оставить jQuery доступным как глобальный.

person Sean McMillan    schedule 11.01.2013
comment
Спасибо, Шон, я знаю, что здесь происходит, и я хочу правильно использовать require.js, где ни один из модулей не должен создавать глобальную переменную, а все остальные модули загружают jQuery как зависимость, если это необходимо. - person rochal; 13.01.2013
comment
В этом случае вы не сможете использовать неконвертированные плагины. Если вы хотите, чтобы jQuery был доступен только как модуль, вам придется преобразовать плагины для использования define(), чтобы объявить зависимость от jQuery. - person Sean McMillan; 14.01.2013
comment
Спасибо, Шон, у меня сложилось впечатление, что shim config по-прежнему позволяет вам обрабатывать модули, не поддерживающие AMD, без использования глобальных зависимостей. ИМХО, это отстой, что вам нужно изменить сторонний код, чтобы заставить его работать с AMD/require.. - person rochal; 15.01.2013
comment
Shim позволяет превратить обычный скрипт в модуль, но не позволяет получить зависимости. Если вы оставите jQuery глобальным, это сработает. Жаль, что сторонние продукты не работают с AMD. Когда jQuery принял его, у меня были большие надежды. И когда Backbone удалил его, стало ясно, что массовое внедрение в ближайшее время не произойдет. - person Sean McMillan; 15.01.2013

Во-первых, убедитесь, что «path/to/jquery-myplugin» действительно расширяет window.jQuery, а не $

noConflict() оставляет объект window.jQuery определенным, но отвязывает себя от window.$ В некоторых новых браузерах window.$ встроен в псевдоним собственной функции document.querySelectorAll.

Во-вторых, вашему myplugin НЕ нужно возвращать себя, так как он не может использоваться сам по себе. Поскольку он расширяет jQuery, возвращайте jQuery из вызова myplugin.

Наконец, «path/to/jquery-myplugin» НЕ является модулем. Это обычный JS-файл. Возможно, RequireJS пытается загрузить его как модуль и не находит define() вызова, что приводит к путанице. Попробуйте добавить расширение файла «.js» к ссылке, чтобы сообщить RequireJS, что ему нужно использовать «js!» плагин для загрузки ресурса.

require.config({
    paths: {
        "jquery": "path/to/jquery",
        "jquery-myplugin": "path/to/jquery-myplugin.js"
    },
    shim: {
        "jquery": {
          init: function() {
             return window.jQuery.noConflict();
          },
        "jquery-myplugin": {
          deps: ['jquery']
          init: function($) {
             return $;
          },
       }
    } 
});
person ddotsenko    schedule 12.01.2013
comment
Это неверно. Код для noConflict находится здесь: javascript noConflict: function( deep ) { if ( window.$ === jQuery ) { window.$ = _$; } if ( deep && window.jQuery === jQuery ) { window.jQuery = _jQuery; } return jQuery; }, Когда вы передаете параметр deep, он также отменяет привязку window.jQuery, поэтому вы можете запускать несколько версий на одной странице. - person p3drosola; 26.11.2013

У меня была такая же проблема, как у вас сегодня. Вот как я мог это исправить:

requirejs.config({
    "baseUrl": "js/lib",
    "paths": {
      "app": "../app"
    }
});

// Final version of jQuery with all needed plugins.
define("jquery", ["jquery-core", "myplugin"], function(jqCore) {
    return jqCore;
});

// Define core jQuery module.
define("jquery-core", ["jquery-1.9.1.min"], function() {
    return jQuery.noConflict(true);
});

// This module exposes jquery module to the global scope and returns previous defined version.
define("jq-global", ["jquery-core"], function(jqCore) {
    var tmp = jQuery;
    jQuery = jqCore;
    return tmp;
});

// Define your plugin here, and don't forget to reset previous jQuery version.
define("myplugin", ["jq-global", "jquery.myplugin"], function(jqGlobal, jqPlugin) {
    jQuery = jqGlobal;
});

// Load the main app module to start the app
requirejs(["app/main"]);

Теперь в моем файле app/main.js я могу сделать следующее:

define(["jquery"], function($) {
    $('body').myplugin();
});

Идея здесь состоит в том, чтобы временно выставить jQuery перед выполнением кода плагина. До сих пор я не тестировал решение в более крупной среде с гораздо большим количеством загружаемых модулей, поэтому я не могу гарантировать, что оно будет работать в долгосрочной перспективе;)

Изменить

Это решение не будет работать!! Поскольку requirejs не загружает сценарии последовательно, возможно, файл js плагина загружается до jquery, что приведет к сбою выполнения. Прошу прощения.

Если у кого-то есть другая идея...

person Bob G    schedule 10.05.2013