Проблема 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
, после этого вы должны быть в порядке, загружая свой собственный код и не получая помехи.