jQuery.ui не определен после оптимизации requirejs

У меня есть несколько файлов JavaScript в моем веб-приложении, которые ранее были загружены через теги <script> в порядке относительных зависимостей. Теперь я реорганизую их через require.js, чтобы накопить и минифицировать их для производства.

Для начала я хотел бы просто загрузить все файлы в глобальный (оконный) контекст, пока без инкапсуляции AMD:

require([
    'jquery'], function() {
    require([
        'jquery-ui'], function() {
        require([
            'jquery-ui.touch-punch',
            // [...]
        ]);
    });
});

Идея здесь в том, что 'jquery' определяет глобальную (контекст окна) переменную jQuery, 'jquery-ui' устанавливает jQuery.ui, а 'jquery-ui.touch-punch' изменяет jQuery.ui (для лучшей поддержки сенсорного ввода).

Это хорошо работает, когда работает как есть, то есть без оптимизации. Однако после компиляции в один файл и минимизации с помощью оптимизатора RequireJS возникает следующая ошибка:

Uncaught TypeError: Cannot read property 'mouse' of undefined

Это происходит на линии, пытающейся получить доступ к jQuery.ui.mouse.

Внутри консоли браузера jQuery правильно установлено в контексте окна, но jQuery.ui не определено. Однако ручное выполнение require(['jquery-ui']) устанавливает jQuery.ui как и ожидалось.

Похоже, что пользовательский интерфейс jQuery после оптимизации ведет себя по-другому, но я не понимаю, как именно и почему. Что я делаю не так?


person Cedric Reichenbach    schedule 04.12.2016    source источник


Ответы (1)


Совок

Установите зависимости между модулями, отличными от AMD, используя shim, а не через вложенные вызовы require (которые на самом деле не устанавливают зависимости). Не забудьте также использовать wrapShim: true в конфигурации, которую вы даете r.js.

Объяснение

Один или несколько модулей, которые вы пытаетесь загрузить, не являются модулями AMD, но фактически вы не определяете зависимости между модулями, отличными от AMD.

Когда вы вкладываете require вызовы, как вы это делаете, вы принудительно устанавливаете порядок, в котором некоторые модули загружаются во время выполнения, но на самом деле это не устанавливает зависимость между модулями. Таким образом, эта стратегия работает до тех пор, пока модули не оптимизированы, но может дать сбой после оптимизации.

Есть только два способа установить зависимости:

  1. Путем передачи массива зависимостей вызову define. Этот метод используется модулями, фактически написанными для спецификации AMD. (RequireJS также поддерживает использование CommonJS-способа запроса модулей, но за кулисами он преобразуется в вызов define с массивом зависимостей. Таким образом, это не третий способ.)

  2. Установив shim список зависимостей. Этот метод предназначен для модулей, отличных от AMD.

Если вы не установите shim, оптимизатор может упорядочить модули не AMD в любом порядке. jquery-ui.touch-punch может стоять перед jquery-ui и jquery в оптимизированном файле, потому что нет никаких причин, по которым этого не должно быть, и тогда вы столкнетесь с проблемами. Если вы посмотрите на код этого плагина, вы увидите, что он не поддерживает AMD. Он просто ожидает наличия jQuery и пользовательского интерфейса jQuery и завершится ошибкой, если их нет.

Когда вы устанавливаете shim, вы устанавливаете порядок модулей, отличных от AMD, в оптимизированном файле.

Вам нужно wrapShim, потому что jquery-ui.touch-punch при использовании версии пользовательского интерфейса jQuery с поддержкой AMD. В противном случае фабрика пользовательского интерфейса jQuery не будет запущена до того, как она понадобится плагину.

person Louis    schedule 04.12.2016
comment
Большое спасибо. Я пробовал со следующей конфигурацией прокладки: shim: {'jquery-ui': {deps: ['jquery']}, 'jquery-ui.touch-punch': {deps: ['jquery-ui']}}, но проблема осталась та же. Отладка показывает, что factory jquery-ui, который установил бы $.ui, никогда не выполняется после оптимизации, хотя define('jquery-ui', ["jquery"], factory) запускается. - person Cedric Reichenbach; 04.12.2016
comment
Вы убедились, что shim присутствует как в конфигурации времени выполнения, так и в конфигурации сборки? Недостаточно, чтобы файл, содержащий вызов require.config, был частью сборки. Вам нужно явно указать shim в конфигурации сборки или (лучше, IMO) установить параметр mainConfigFile, чтобы он указывал на файл с require.config. - person Louis; 05.12.2016
comment
Да, я заверил это раньше. Но отсутствовала еще одна часть конфигурации: wrapShim: false. AFAIU, это связано с тем, что jQueryUI требует от AMD jQuery, но зависит от сенсорного удара, отличного от AMD. Упомянутая настройка, по-видимому, оборачивает Touch-Punch в модуль AMD для обеспечения правильного порядка загрузки (документация). Может быть, вы можете добавить это к своему ответу для полноты, и я приму это. - person Cedric Reichenbach; 05.12.2016
comment
Вы изначально использовали wrapShim: true в своей конфигурации? Если нет, то добавление wrapShim: false не должно иметь значения, потому что false используется по умолчанию, если вы не устанавливаете wrapShim. - person Louis; 05.12.2016
comment
О, простите, это была опечатка. Теперь я использую wrapShim: true, чтобы заставить его работать, и раньше его не было (т.е. по умолчанию false). - person Cedric Reichenbach; 06.12.2016
comment
О да. Мои сценарии использования немного отличаются, поэтому я забыл об этой проблеме. Я отредактировал свой ответ. - person Louis; 06.12.2016