Проблема RequireJs при загрузке нескольких версий jQuery

На моем веб-сайте я загружаю большинство JavaScript асинхронно с помощью RequireJs. Для моей настройки RequireJs см. Следующее:

require.config({
    paths: {
        'jquery': '//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery'
    },
    shim: {
        'jquery.accordion': {
            deps: ['jquery']
        }
    }
});

Скажем, у меня есть следующий код, определенный внутри тела, для загрузки файла асинхронно:

require(['DisplayAccordion']);

Где DisplayAccordion.js содержит следующее:

define(['jquery', 'jquery.accordion'], function($) {
    $(function() {
        $('.xyz').accordion();
    });
});

Примечание. jquery.accordion - это просто плагин jQuery, который не поддерживает AMD и требует определения глобальной переменной jQuery.

Это отлично работает, но теперь я говорю о том, что я опускаю ссылку script на своей странице в стороннюю библиотеку. Например:

<script src="//example.com/ThirdParty.js"></script>

Если библиотека сторонних разработчиков загружает собственную версию jQuery. Теперь я получаю сообщение об ошибке:

Объект не поддерживает свойство или метод "аккордеон".

После прохождения кода я обнаружил, что он выполняется в следующем порядке:

  • ThirdParty.js
  • jquery.min.js - сторонняя версия
  • jquery.min.js - моя версия
  • jquery.accordion.js - где $указывает на мою ссылку на версию jQuery
  • DisplayAccordion.js(функция обратного вызова) - где $указывает на стороннюю версию jQuery

Теперь я могу понять, почему я получаю ошибку, потому что плагин прикреплен к другому объекту. Однако я не уверен, почему это сделало бы это.

В приведенной ниже информации просто объясняется, почему использование $.noConflict(true) не будет работать.

После некоторых исследований по этому вопросу. Я изменил свою конфигурацию на:

require.config({
    paths: {
        'jquery': '//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery'
    },
    map: {
        '*': { 'jquery': 'jquery-private' },
        'jquery-private': { 'jquery': 'jquery' }
    },
    shim: {
        'jquery.accordion': {
            deps: ['jquery']
        }
    }
});

Где jquery-private.js определяется как:

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

Обратите внимание, что это было взято из http://www.requirejs.org/docs/jquery.html#noconflictmap

Теперь он выполняется в следующем порядке:

  • ThirdParty.js
  • jquery.min.js - сторонняя версия
  • jquery-private.js(функция обратного вызова)
  • jquery.min.js - моя версия
  • jquery.accordion.js - где $is undefined
  • DisplayAccordion.js(функция обратного вызова) - где $указывает на стороннюю версию JQuery

Как вы можете себе представить, это не сработает, так как $is undefined в файле jquery.accordion.js.

После abit дальнейшей отладки я обнаружил, что сторонняя библиотека также вызывает:

$.noConflict(true);

Думаю, я понимаю, что происходит здесь. Когда он вызывает $.noConflict(true) в сторонней библиотеке, он пытается установить глобальные переменные $и jQuery в предыдущую версию. Однако, поскольку ни одна предыдущая версия не загружена, она установлена ​​на undefined.

Теперь, когда он вызывает jquery-private.js и возвращает $.noConflict(true), он вернет глобальную переменную jQuery, которая была установлена ​​на undefined. Однако теперь он установит глобальную переменную jQuery в стороннюю версию библиотеки.

Итак, когда он загружает jquery.accordion $is undefined. Но когда он вызывает вызовы DisplayAccordion.js, он теперь ссылается на стороннюю версию библиотеки jQuery.

Буду признателен, если кто-нибудь может предложить исправить. Благодаря

Ответы

Ответ 1

Я бы предложил вам создать собственную версию jquery.accordion.js - превратив ее в модуль RequireJS (оберните его в define(["jquery-private"], function($){ ... }); и т.д.)

Все остальное должно запрашивать jquery-private вместо jquery - поэтому единственное упоминание jquery в вызове require()/define() будет в самой jquery-private.

Хотя это означало бы невозможность загрузить его из стороннего автоматически обновленного CDN, это позволило бы запустить его в "правильной" версии jQuery.

Тем временем (если вы знаете, какой из них - GitHub перечисляет несколько проектов этого имени), вы также можете отправить отчет об ошибке/запрос на перенос в GitHub, чтобы сделать его RequireJS способным - затем изменить путь после его обновления: -)

Ответ 2

Единственный способ, которым я вижу поведение, которое вы изначально выполняете, - это загрузить ThirdParty.js после того, как RequireJS был загружен. Вот что происходит:

  • Загружен RequireJS.

  • ThirdParty.js загружается.

  • jquery.min.js Загрузка сторонних версий. Он проверяет, существует ли define.amd. Он делает это, поэтому он вызывает define('jquery', ... и регистрируется как модуль AMD.

  • jquery.min.js загружается ваша версия. Вы можете задаться вопросом, почему RequireJS потрудился с загрузкой этого вообще, если jquery уже определен. Мы можем иметь дело с состоянием гонки здесь. Если есть что-то, что вызвало загрузку jquery до шага 3, я бы ожидал, что RequireJS запланирует бесполезную загрузку вашей версии jQuery. define('jquery', ... будет вызываться снова, но ничего не сделает из-за первого вызова раньше.

  • jquery.accordion.js выполняется, где $ указывает на вашу версию jQuery. Да, потому что на шаге 4 глобальная $ была установлена ​​на вашу версию.

  • DisplayAccordion.js выполняется, где $ указывает на стороннюю версию jQuery. Да, потому что на шаге 3 это версия третьей стороны, зарегистрированная в качестве модуля.

Чтобы исправить это, я бы удостоверился, что ThirdParty.js и его версия загрузки jQuery до RequireJS. Как вы это достигаете, зависит от того, как ThirdParty.js загружает jQuery. Поскольку ThirdParty.js вызывает $.noConflict, после этого вы должны быть в порядке, загружая свой собственный код и не получая помехи.