Просто чтобы прояснить любую путаницу вокруг exports
, предполагается, что любая библиотека прокладок прикрепляет свойство к глобальному контексту (window
или root
) или изменяет уже существующее глобальное свойство (например, плагин jQuery). Когда requireJS получает команду для загрузки зависимостей с оболочкой, он проверяет глобальный контекст на наличие свойства, соответствующего значению exports
этой конфигурации оболочек, и, если находит его, возвращает его как значение этого модуля. Если он не находит его, то загружает связанный скрипт, ждет его выполнения, затем находит глобальный символ и возвращает его.
Важно помнить, что если конфигурация оболочки не содержит значение exports
, любой метод init
в этой конфигурации НЕ будет выполняться. Загрузчик зависимостей должен найти значение для модуля (это то, что указывает exports
), прежде чем этот модуль можно будет инициализировать, поэтому свойство требуется, если для этого модуля существует прокладка init
.
обновление: я также должен указать, что если рассматриваемый модуль вызывает define
где-либо, любая конфигурация прокладки, которая у вас есть для этого модуля, будет проигнорирована. Это на самом деле вызвало у меня некоторые головные боли, потому что я хотел использовать конфигурацию прокладки для вызова метода jQuery jQuery.noConflict(true)
, чтобы отменить глобификацию jQuery и сохранить его только для модулей, которые в нем нуждаются, но не смог заставить его работать. (См. обновление внизу для получения информации о том, как легко сделать это, используя конфигурацию карты вместо конфигурации прокладки.)
обновление 2: недавний вопрос о группе google requireJS заставил меня понять, что мое объяснение может немного вводить в заблуждение, поэтому я хотел бы уточнить. RequireJS будет повторно использовать шиммированную зависимость, только если она была загружена с помощью requireJS хотя бы один раз. То есть, если у вас просто есть тег <script>
на странице хостинга (скажем, например, подчеркивание), вот так:
<script src='lib/underscore.js'></script>
<script src='lib/require.js' data-main='main.js'></script>
... и у вас есть что-то подобное в вашей конфигурации requireJS:
paths: {
'underscore': 'lib/underscore'
},
shim: {
'underscore': {
exports: '_'
}
}
Затем, когда вы в первый раз сделаете define(['underscore'], function (_) {});
или var _ = require('underscore');
, RequireJS повторно загрузит библиотеку подчеркивания, а не повторно использует ранее определенную window._
, потому что, насколько известно requireJS, вы никогда раньше не загружали подчеркивание. Конечно, он может проверить, определен ли уже _
в корневой области, но у него нет возможности убедиться, что _
, который уже есть, совпадает с тем, который определен в вашей конфигурации paths
. Например, и prototype
, и jquery
присваивают себе значение window.$
по умолчанию, и если requireJS предполагает, что 'window.$' является jQuery, тогда как на самом деле это прототип, вы окажетесь в плохой ситуации.
Все это означает, что если вы смешиваете и подбираете такие стили загрузки скрипта, ваша страница будет иметь что-то вроде этого:
<script src='lib/underscore.js'></script>
<script src='lib/require.js' data-main='main.js'></script>
<script src='lib/underscore.js'></script>
Где второй экземпляр подчеркивания загружается с помощью requireJS.
По сути, библиотека должна быть загружена через requireJS, чтобы requireJS знал о ней. Однако в следующий раз, когда вам потребуется подчеркивание, requireJS скажет: «Эй, я уже загрузил это, так что просто верните значение exports
и не беспокойтесь о загрузке другого скрипта».
Это означает, что у вас есть два реальных варианта. Один из них я бы назвал анти-шаблоном: просто не используйте requireJS для выражения зависимостей для глобальных скриптов. То есть, пока библиотека прикрепляет глобальный контекст к корневому контексту, вы сможете получить к нему доступ, даже если эта зависимость не требуется явно. Вы можете понять, почему это анти-шаблон - вы просто устранили большинство преимуществ использования загрузчика AMD (явный список зависимостей и переносимость).
Другой, лучший вариант — использовать requireJS для загрузки всего до такой степени, что единственный фактический тег сценария, который вы должны создать самостоятельно, — это тот, который изначально загружает requireJS. Вы можете использовать прокладки, но в 95% случаев не так уж сложно вместо этого добавить в скрипт оболочку AMD. Может потребоваться немного больше усилий, чтобы преобразовать все ваши не-AMD-библиотеки в совместимые с AMD, но как только вы сделаете одну или две, все станет намного проще — я могу взять любой универсальный плагин jQuery и преобразовать его в модуль AMD. менее чем за минуту. Обычно достаточно просто добавить
define(['jquery'], function (jQuery) {
вверху и
return jQuery;
});
внизу. Причина, по которой у меня сопоставление «jquery» с jQuery
, а не $
, заключается в том, что я заметил, что большинство плагинов в наши дни заключены в закрытие, подобное этому:
(function ($) {
// plugin code here
})(jQuery);
И это хорошая идея, чтобы обратить внимание на предполагаемый объем. Вы, безусловно, можете напрямую сопоставить «jquery» с $
, предполагая, что плагин не ожидает найти jQuery
вместо $
. Это всего лишь базовая оболочка AMD — более сложные обычно пытаются определить, какой тип загрузчика используется (commonJS, AMD или обычные глобальные) и используют другой метод загрузки в зависимости от результата. Вы можете легко найти примеры этого за несколько секунд в Google.
Обновление: обходной путь, который я использовал для поддержки использования jQuery.noConflict(true)
с RequireJS, сработал, но потребовал очень небольшой модификации исходного кода jQuery, и с тех пор я нашел гораздо лучший способ выполнить то же самое без изменения jQuery. К счастью, то же самое сделал и Джеймс Берк, автор RequireJS, который добавил его в документацию по RequireJS: http://requirejs.org/docs/jquery.html#noconflictmap
person
Isochronous
schedule
26.02.2013