Загрузка второй версии jQuery и других библиотек вместе с ней

Я делаю виджет, который состоит из фрагмента кода для размещения на вашем сайте, который затем будет извлекать контент через js. Вот пример фрагмента:

<div data-token="bc1cea6304e8" id="my-widget">
    <script src="http://localhost:8080/assets/eg-widget.js" type="text/javascript"></script>
</div>

Я делаю тест, чтобы проверить, есть ли на странице jQuery, и если да, то версия не ниже 1.5.

if (typeof jQuery == 'undefined' || isVersion("1.5", ">")) {

    var script_tag = document.createElement('script');
    script_tag.setAttribute("type", "text/javascript");
    script_tag.setAttribute("src",
        "http://ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js");

    // Try to find the head, otherwise default to the documentElement
    (document.getElementsByTagName("head")[0] || document.documentElement).appendChild(script_tag);

    if (script_tag.readyState) {
        script_tag.onreadystatechange = function () { // For old versions of IE
            if (this.readyState == 'complete' || this.readyState == 'loaded') {
                scriptLoadHandler();
            }
        };
    } else { // Other browsers
        script_tag.onload = scriptLoadHandler;
    }
} else {
    // The jQuery version on the window is the one we want to use
    jQueryEg = window.jQuery;
    scriptLoadHandler();
}

В любом случае мы устанавливаем переменную jQueryEg для jQuery, который мы решили использовать, чтобы избежать конфликтов.

Если jQuery еще не загружен, все отлично. Но если загружается более старая версия, мы сталкиваемся с конфликтами при попытке загрузить другие библиотеки, такие как jQuery-ui.

Для этого мы устанавливаем window.jQuery в наш недавно загруженный jQuery, чтобы при загрузке jquery-ui и bootstrap.js их ссылки на window.jQuery или $ указывали на правильную версию.

Проблема в том, что иногда другие части страницы вызывают jQuery одновременно с тем, что мы установили window.jQuery для нашей версии, что вызывает проблемы.

Есть мысли о том, как мы можем избежать этих конфликтов?

jQueryEg = window.jQuery.noConflict(true);
window.jQueryEg = jQueryEg;

// jquery-ui reference window.jQuery so we need to set that to our new jquery for a sec

if (jQueryEg.ui == undefined || jQueryEg.ui.datepicker == undefined) {
    var otherjQuery = window.jQuery;
    window.jQuery = jQueryEg;

    var script_tag = document.createElement('script');
    script_tag.setAttribute("type", "text/javascript");
    script_tag.setAttribute("src",
        "//ajax.googleapis.com/ajax/libs/jqueryui/1.8.23/jquery-ui.min.js");
    (document.getElementsByTagName("head")[0] || document.documentElement).appendChild(script_tag);

    script_tag.onload = function () {
        window.jQuery = otherjQuery;
        main();
    };

}

2 ответа

Решение

Столкнувшись с этой точной ситуацией прежде, в конце концов, определение было тем, что вы, вероятно, уже знаете, и не хотите слышать: просто не существует хорошего способа избежать всех крайних случаев при загрузке нескольких версий внешних библиотек, подобных этому, по причинам, которые вы уже указали. Краткое описание осложнений:

  • Конечно, главная проблема: загрузка внешних сценариев по своей природе асинхронна, и нет обратного вызова "непосредственно перед выполнением" / "сразу после выполнения", чтобы гарантировать, что все упаковано красиво. То есть: нет способа "окружить" динамически загружаемый код.
  • Скрипты выполняются по порядку, когда помещаются в документ напрямую, но нет способа условно загрузить один из них на основе чего-то, определенного другим.
  • И, наконец, по соображениям удобства использования (т. Е. Разрешению случайным людям по всему Интернету использовать ваш фрагмент с нулевым знанием кода и без поддержки), такие ограничения, как "убедитесь, что этот фрагмент вставлен перед любыми другими сценариями!" прямо Даже если бы они могли работать (а они не увидят других двух пунктов), их бы проигнорировали, особенно те, кто не имеет полного контроля над своими страницами.

К сожалению, единственный ответ очевиден: размещайте измененные версии библиотек на своих собственных серверах.

Конечно, вам не нужно постоянно обслуживать эти версии, поэтому вы все равно сможете воспользоваться CDN от Google. Между тем, вы все еще можете использовать свой собственный CDN для доставки модифицированных версий. Основными руководящими принципами являются:

  1. Запустите ваш сценарий после загрузки всей страницы (используя onDOMReady и т. Д.), Чтобы убедиться, что конфликт не возникает "после" включения вашего собственного сценария.
  2. Определите, возможен ли конфликт. В этих случаях обслуживайте свои измененные версии и в противном случае работайте с общедоступной CDN.
  3. Часто проверяйте совместимость с другими библиотеками. Ваш код будет развиваться с течением времени, так же как и библиотеки, которые вы используете. Шаг "определить, возможен ли конфликт" должен быть точным, чтобы это работало. Ошибка на стороне предостережения: представьте свою версию, если вы не уверены.
  4. Всегда предоставляйте пастеру возможность легко сказать "всегда используйте модифицированную версию", если ваше обнаружение не поможет.
  5. Всегда обслуживайте сжатые версии ваших скриптов с соответствующими заголовками кэша, чтобы уменьшить нагрузку на ваши собственные серверы.

Обычно вы обнаружите, что количество обращений к вашему собственному серверу на самом деле весьма минимально, сравнительно. Как всегда, не верьте мне: ведите статистику по своим собственным, реальным сценариям!

Однажды я столкнулся с подобной проблемой. Глядя на код jQuery-UI, я заметил, что все это обернуто в немедленно вызванное выражение функции (или, может быть, я обернул его сам? Не помню):

( function ( jQuery, undefined ){
  // Code here...
})( jQuery );

создавать частные области. Так что мое решение было

  1. Сохранить текущий jQuery в jQuery_OLD
  2. Загрузите новую версию jQuery
  3. Загрузите jQuery-UI и предоставьте совместимую версию
  4. Восстановить старое jQuery

Таким образом, следующее дает идею

<script src="jquery-1.4.js"></script>
<script>
    var jQuery_1_4 = jQuery.noConflict();
</script>
<script src="jquery-1.8.js"> </script>
<!-- load jquery UI -->
<script src="jquery-ui.js"> </script>
<!-- restore window.jQuery -->
<script>
    jQuery = jQuery_1_4;
</script>
Другие вопросы по тегам