Spec-Zone.ru › RequireJS

API RequireJS

  • Использование
    • Загрузка JavaScript-файлов
    • Точка входа data-main
    • Определение модуля
      • Простые пары имя/значение
      • Функции определения
      • Функции определения с зависимостями
      • Определение модуля как функции
      • Определение модуля с упрощённым оболочкой CommonJS
      • Определение модуля с именем
      • Примечания по модулям
      • Циклические зависимости
      • Указание зависимости JSONP-службы
      • Удаление определения модуля
  • Механизм
  • Параметры конфигурации
  • Расширенное использование
    • Загрузка модулей из пакетов
    • Поддержка многоверсионности
    • Загрузка кода после загрузки страницы
    • Поддержка веб-воркеров
    • Поддержка Rhino
    • Поддержка Nashorn
    • Обработка ошибок
  • Плагины загрузчика
    • Указание зависимости текстового файла
    • Поддержка событий загрузки страницы/DOM готов
    • Определение набора I18N

Использование

Загрузка JavaScript-файлов

RequireJS использует другой подход к загрузке скриптов, чем традиционные теги <script>. Хотя он также может работать быстро и эффективно оптимизироваться, основной целью является поощрение модульного кода. В рамках этого подхода используется идентификаторы модулей вместо URL-адресов для тегов скриптов.

RequireJS загружает весь код относительно baseUrl. baseUrl обычно настроен на ту же директорию, что и скрипт, используемый в атрибуте data-main для загрузки скрипта верхнего уровня для страницы. Атрибут data-main — это специальный атрибут, который require.js будет проверять для начала загрузки скриптов. Этот пример приведет к baseUrl scripts:

<!--This sets the baseUrl to the "scripts" directory, and
    loads a script that will have a module ID of 'main'-->
<script data-main="scripts/main.js" src="scripts/require.js"></script>

Или baseUrl можно установить вручную через конфигурацию RequireJS. Если нет явной конфигурации и data-main не используется, то значение baseUrl по умолчанию — директория, содержащая HTML-страницу, на которой используется RequireJS.

RequireJS также по умолчанию предполагает, что все зависимости являются скриптами, поэтому он не ожидает видеть суффикс ".js" в идентификаторах модулей. RequireJS автоматически добавляет его при переводе идентификатора модуля в путь. С помощью конфигурации paths вы можете установить расположения группы скриптов. Все эти возможности позволяют использовать более короткие строки для скриптов по сравнению с традиционными тегами <script>.

Иногда вам может потребоваться напрямую ссылаться на скрипт, не подчиняясь правилам "baseUrl + paths" для его поиска. Если идентификатор модуля имеет одно из следующих свойств, идентификатор не будет обрабатываться с помощью конфигурации "baseUrl + paths" и будет обрабатываться как обычный URL, относительный к документу:

  • Оканчивается на ".js".
  • Начинается с "/".
  • Содержит протокол URL, такой как "http:" или "https:".

В целом, лучше использовать baseUrl и конфигурацию "paths" для настройки путей идентификаторов модулей. Это даст вам больше гибкости в переименовании и настройке путей к различным расположениям для оптимизированных сборок.

Аналогично, чтобы избежать большого количества конфигураций, лучше избегать глубоких иерархий папок для скриптов и вместо этого либо хранить все скрипты в baseUrl, либо, если вы хотите разделить код библиотеки/поставщика от вашего приложения, использовать структуру каталогов подобную этой:

  • www/
    • index.html
    • js/
      • app/
        • sub.js
      • lib/
        • jquery.js
        • canvas.js
      • app.js
      • require.js

в index.html:

<script data-main="js/app.js" src="js/require.js"></script>

и в app.js:

requirejs.config({
    //By default load any module IDs from js/lib
    baseUrl: 'js/lib',
    //except, if the module ID starts with "app",
    //load it from the js/app directory. paths
    //config is relative to the baseUrl, and
    //never includes a ".js" extension since
    //the paths config could be for a directory.
    paths: {
        app: '../app'
    }
});

// Start the main app logic.
requirejs(['jquery', 'canvas', 'app/sub'],
function   ($,        canvas,   sub) {
    //jQuery, canvas and the app/sub module are all
    //loaded and can be used here now.
});

Обратите внимание, что в этом примере номера версий библиотек, таких как jQuery, не были включены в имена файлов. Рекомендуется хранить эту информацию о версиях в отдельном текстовом файле, или если вы используете инструмент, такой как volo, он будет отмечать package.json информацией о версии, но сохранит файл на диске как "jquery.js". Это позволяет использовать минимальную конфигурацию вместо необходимости вносить запись в конфигурацию "paths" для каждой библиотеки. Например, настройте "jquery" на "jquery-1.7.2".

В идеале, загружаемые скрипты будут модулями, определёнными вызовом define(). Однако, возможно, вам придётся использовать традиционные/устаревшие скрипты "глобальных переменных браузера", которые не описывают свои зависимости с помощью define(). Для них можно использовать конфигурацию shim для правильного описания их зависимостей.

Если вы не укажете зависимости, у вас, скорее всего, возникнут ошибки загрузки, так как RequireJS загружает скрипты асинхронно и вне очереди для скорости.

Точка входа data-main

Атрибут data-main — это специальный атрибут, который require.js будет проверять для начала загрузки скриптов:

<!--when require.js loads it will inject another script tag
    (with async attribute) for scripts/main.js-->
<script data-main="scripts/main" src="scripts/require.js"></script>

Вы обычно используете скрипт с data-main для установки параметров конфигурации и последующей загрузки первого модуля приложения. Примечание: тег скрипта, который require.js генерирует для вашего модуля data-main, включает атрибут async. Это означает, что вы не можете предполагать, что загрузка и выполнение вашего скрипта data-main завершатся до других скриптов, указанных позже на этой же странице.

Например, такая структура может работать непредсказуемо, когда путь к модулю 'foo' в конфигурации require.config не установлен до его require() позже:

<script data-main="scripts/main" src="scripts/require.js"></script>
<script src="scripts/other.js"></script>
// contents of main.js:
require.config({
    paths: {
        foo: 'libs/foo-1.1.3'
    }
});
// contents of other.js:

// This code might be called before the require.config() in main.js
// has executed. When that happens, require.js will attempt to
// load 'scripts/foo.js' instead of 'scripts/libs/foo-1.1.3.js'
require(['foo'], function(foo) {

});

Если вы хотите выполнять вызовы require() на странице HTML, то лучше не использовать data-main. data-main предназначен только для использования, когда на странице есть только одна основная точка входа — скрипт data-main. Для страниц, которые хотят выполнять встраиваемые вызовы require() , лучше вложить их внутрь вызова require() для конфигурации:

<script src="scripts/require.js"></script>
<script>
require(['scripts/config'], function() {
    // Configuration loaded now, safe to do other require calls
    // that depend on that config.
    require(['foo'], function(foo) {

    });
});
</script>

Определение модуля

Модуль отличается от традиционного файла скрипта тем, что он определяет хорошо структурированный объект, который избегает загрязнения глобального пространства имён. Он может явно перечислять свои зависимости и получить доступ к этим зависимостям без необходимости ссылок на глобальные объекты, а вместо этого получает зависимости как аргументы функции, определяющей модуль. Модули в RequireJS — это расширение паттерна модуля с преимуществом отсутствия необходимости в глобальных переменных для обращения к другим модулям.

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

(Если вы знакомы с модулями CommonJS или используете их, см. также Примечания по CommonJS для получения информации о том, как формат модулей RequireJS соответствует модулям CommonJS).

В файле на диске должен быть только один модуль.

Простые пары имя/значение

Если модуль не имеет зависимостей и представляет собой набор пар имя/значение, передайте литерал объекта в define():

//Inside file my/shirt.js:
define({
    color: "black",
    size: "unisize"
});

Функции определения

Если модуль не имеет зависимостей, но ему требуется функция для выполнения некоторых подготовительных работ, передайте функцию в define():

//my/shirt.js now does setup work
//before returning its module definition.
define(function () {
    //Do setup work here

    return {
        color: "black",
        size: "unisize"
    }
});

Функции определения с зависимостями

Если у модуля есть зависимости, первым аргументом должна быть массив имён зависимостей, а вторым — функция определения. Функция будет вызвана для определения модуля, как только все зависимости будут загружены. Функция должна вернуть объект, определяющий модуль. Зависимости будут переданы функции определения как аргументы функции, в том же порядке, что и в массиве зависимостей:

//my/shirt.js now has some dependencies, a cart and inventory
//module in the same directory as shirt.js
define(["./cart", "./inventory"], function(cart, inventory) {
        //return an object to define the "my/shirt" module.
        return {
            color: "blue",
            size: "large",
            addToCart: function() {
                inventory.decrement(this);
                cart.add(this);
            }
        }
    }
);

В этом примере создаётся модуль my/shirt. Он зависит от my/cart и my/inventory. Файлы на диске структурированы следующим образом:

  • my/cart.js
  • my/inventory.js
  • my/shirt.js

Вызов функции выше указывает два аргумента "cart" и "inventory". Это модули, представленные именами модулей "./cart" и "./inventory".

Функция не вызывается, пока модули my/cart и my/inventory не будут загружены, а функция получает модули как аргументы "cart" и "inventory".

Явно не рекомендуется определять модули, создающие глобальные переменные, чтобы на странице можно было одновременно использовать несколько версий модуля (см. Расширенное использование). Также порядок аргументов функции должен соответствовать порядку зависимостей.

Возвращаемый функцией объект определяет модуль "my/shirt". Определяя модули таким образом, "my/shirt" не существует как глобальный объект.

Определение модуля как функции

Модули не обязаны возвращать объекты. Любое допустимое возвращаемое значение функции допускается. Вот модуль, который возвращает функцию как определение модуля:

//A module definition inside foo/title.js. It uses
//my/cart and my/inventory modules from before,
//but since foo/title.js is in a different directory than
//the "my" modules, it uses the "my" in the module dependency
//name to find them. The "my" part of the name can be mapped
//to any directory, but by default, it is assumed to be a
//sibling to the "foo" directory.
define(["my/cart", "my/inventory"],
    function(cart, inventory) {
        //return a function to define "foo/title".
        //It gets or sets the window title.
        return function(title) {
            return title ? (window.title = title) :
                   inventory.storeName + ' ' + cart.name;
        }
    }
);

Определение модуля с упрощённым оболочкой CommonJS

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

define(function(require, exports, module) {
        var a = require('a'),
            b = require('b');

        //Return the module value
        return function () {};
    }
);

Эта оболочка полагается на Function.prototype.toString() для предоставления полезного строкового значения содержимого функции. Это не работает на некоторых устройствах, таких как PS3 и некоторых старых мобильных браузерах Opera. Используйте оптимизатор для извлечения зависимостей в формате массива для использования на этих устройствах.

Дополнительную информацию можно найти на странице CommonJS и в разделе "Sugar" в разделе "Почему AMD".

Определение модуля с именем

Вы можете столкнуться с вызовами define(), которые включают имя модуля в качестве первого аргумента:

    //Explicitly defines the "foo/title" module:
    define("foo/title",
        ["my/cart", "my/inventory"],
        function(cart, inventory) {
            //Define foo/title object in here.
       }
    );

Обычно это генерируется инструментом оптимизации. Вы можете явно задавать имена модулей, но это делает их менее переносимыми — если вы переместите файл в другой каталог, вам нужно будет изменить имя. Обычно лучше избегать кодирования имени модуля и просто позволить инструменту оптимизации заполнить имена модулей. Инструмент оптимизации должен добавлять имена, чтобы можно было объединить несколько модулей в один файл, что позволит ускорить загрузку в браузере.

Другие заметки о модулях

Один модуль на файл: в файле JavaScript должен быть определён только один модуль, учитывая алгоритм сопоставления имени модуля с путём к файлу. Вы должны использовать инструмент оптимизации для группировки нескольких модулей в оптимизированные файлы.

Относительные имена модулей внутри define(): для вызовов require("./relative/name"), которые могут происходить внутри вызова функции define(), обязательно запрашивайте «require» в качестве зависимости, чтобы относительное имя было разрешено правильно:

define(["require", "./relative/name"], function(require) {
    var mod = require("./relative/name");
});

Или, ещё лучше, используйте сокращённый синтаксис, доступный для использования с модулями преобразования CommonJS:

define(function(require) {
    var mod = require("./relative/name");
});

Этот формат будет использовать Function.prototype.toString() для поиска вызовов require() и добавления их в массив зависимостей вместе с «require», поэтому код будет правильно работать с относительными путями.

Относительные пути действительно полезны, если вы создаёте несколько модулей в каталоге, чтобы вы могли делиться каталогом с другими пользователями или другими проектами, и вы хотите иметь возможность получить доступ к модулям-близнецам в этом каталоге, не зная имени каталога.

Относительные имена модулей относительны к другим именам, а не путям: загрузчик хранит модули по их имени, а не по их пути. Таким образом, для ссылок на относительные имена они разрешаются относительно имени модуля, производящего ссылку, а затем это имя модуля или идентификатор преобразуется в путь, если его нужно загрузить. Пример кода для пакета «compute», в котором есть модули «main» и «extras»:

* lib/
    * compute/
        * main.js
        * extras.js

где модуль main.js выглядит следующим образом:

define(["./extras"], function(extras) {
    //Uses extras in here.
});

Если это была конфигурация путей:

require.config({
    baseUrl: 'lib',
    paths: {
      'compute': 'compute/main'
    }
});

И выполняется require(['compute']), то lib/compute/main.js будет иметь имя модуля «compute». Когда он запрашивает './extras', это разрешается относительно «compute», то есть «compute/./extras», что сводится к просто «extras». Поскольку в конфигурации путей нет имени модуля, сгенерированный путь будет для 'lib/extras.js', что является неверным.

В данном случае конфигурация пакетов — лучший вариант, поскольку она позволяет настроить основной модуль как «compute», но внутренне загрузчик будет хранить модуль с идентификатором «compute/main», чтобы ссылка «./extras» работала.

Другой вариант — создать модуль в lib/compute.js, который просто define(['./compute/main'], function(m) { return m; });, тогда нет необходимости в конфигурации путей или пакетов.

Или, не устанавливайте конфигурацию путей или пакетов и выполните вызов require верхнего уровня как require(['compute/main']).

Генерировать URL относительно модуля: Возможно, вам нужно сгенерировать URL, относительный к модулю. Для этого запросите «require» в качестве зависимости, а затем используйте require.toUrl() для генерации URL:

define(["require"], function(require) {
    var cssUrl = require.toUrl("./style.css");
});

Отладка консоли: если вам нужно работать с модулем, который вы уже загрузили с помощью вызова require(["module/name"], function(){}) в консоли JavaScript, вы можете использовать форму require(), которая просто использует строковое имя модуля для его получения:

require("module/name").callSomeFunction()

Обратите внимание, что это работает только в том случае, если «module/name» был ранее загружен с помощью асинхронной версии require: require(["module/name"]). Если используется относительный путь, как './module/name', они работают только внутри define

Циклические зависимости

Если вы определяете циклическую зависимость («a» нуждается в «b», а «b» — в «a»), в этом случае, когда вызывается функция модуля «b», она получит неопределённое значение для «a». «b» может получить доступ к «a» позже, после определения модулей, используя метод require() (обязательно укажите require в качестве зависимости, чтобы использовался правильный контекст для поиска «a»):

//Inside b.js:
define(["require", "a"],
    function(require, a) {
        //"a" in this case will be null if "a" also asked for "b",
        //a circular dependency.
        return function(title) {
            return require("a").doSomething();
        }
    }
);

Обычно вам не нужно использовать require() для получения модуля, а вместо этого полагайтесь на то, что модуль передаётся в функцию в качестве аргумента. Циклические зависимости редки и обычно указывают на то, что вы можете пересмотреть дизайн. Однако иногда они необходимы, и в этом случае используйте require() как указано выше.

Если вы знакомы с модулями CommonJS, вы можете вместо этого использовать exports для создания пустого объекта для модуля, который доступен немедленно для ссылки другими модулями. Сделав это с обеих сторон циклической зависимости, вы можете безопасно сохранить другой модуль. Это работает только в том случае, если каждый модуль экспортирует объект для значения модуля, а не функцию:

//Inside b.js:
define(function(require, exports, module) {
    //If "a" has used exports, then we have a real
    //object reference here. However, we cannot use
    //any of "a"'s properties until after "b" returns a value.
    var a = require("a");

    exports.foo = function () {
        return a.bar();
    };
});

Или, если вы используете подход с массивом зависимостей, запросите специальную зависимость 'exports':

//Inside b.js:
define(['a', 'exports'], function(a, exports) {
    //If "a" has used exports, then we have a real
    //object reference here. However, we cannot use
    //any of "a"'s properties until after "b" returns a value.

    exports.foo = function () {
        return a.bar();
    };
});

Указание зависимости JSONP-сервиса

JSONP — это способ вызова некоторых сервисов на JavaScript. Он работает через домены и является устоявшимся подходом к вызову сервисов, которые просто требуют HTTP GET через тег script.

Чтобы использовать JSONP-сервис в RequireJS, укажите «define» в качестве значения параметра обратного вызова. Это означает, что вы можете получить значение JSONP-URL так, как если бы это было определение модуля.

Вот пример вызова конечной точки JSONP-API. В этом примере параметр обратного вызова JSONP называется «callback», поэтому «callback=define» говорит API обернуть ответ JSON в оболочку «define()»:

require(["http://example.com/api/data.json?callback=define"],
    function (data) {
        //The data object will be the API response for the
        //JSONP data call.
        console.log(data);
    }
);

Это использование JSONP должно быть ограничено JSONP-сервисами для начальной настройки приложения. Если JSONP-сервис превышает время ожидания, это означает, что другие модули, определённые через define(), могут не быть выполнены, поэтому обработка ошибок не является надёжной.

Поддерживаются только JSONP-значения возврата, которые являются объектами JSON. JSONP-ответ, который представляет собой массив, строку или число, не будет работать.

Эта функциональность не должна использоваться для долговременных JSONP-соединений для потоковой передачи данных в реальном времени. Такие API должны выполнять больше очистки скриптов после получения каждого ответа, а RequireJS загрузит JSONP-URL только один раз — последующие использования того же URL в качестве зависимости в require() или define() вызове получат кэшированное значение.

Ошибки при загрузке JSONP-сервиса обычно возникают из-за превышения времени ожидания для сервиса, так как загрузка тега script не даёт подробной информации о проблемах с сетью. Чтобы обнаруживать ошибки, вы можете переопределить requirejs.onError(), чтобы получить ошибки. Более подробная информация содержится в разделе Обработка ошибок.

Удаление определения модуля

Существует глобальная функция requirejs.undef(), которая позволяет удалить определение модуля. Она сбросит внутреннее состояние загрузчика, чтобы забыть предыдущее определение модуля.

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

Если вы хотите выполнить более сложный анализ графа зависимостей для удаления определений, полуоткрытый API onResourceLoad может быть полезен.

Механизмы

RequireJS загружает каждую зависимость в виде тега script, используя head.appendChild().

RequireJS ждёт загрузки всех зависимостей, определяет правильный порядок вызова функций, определяющих модули, затем вызывает функции определения модулей после вызова зависимостей для этих функций. Обратите внимание, что зависимости для данной функции определения модуля могут вызываться в любом порядке из-за их отношений зависимостей и порядка загрузки через сеть.

Использование RequireJS в серверной среде JavaScript со синхронной загрузкой должно быть таким же простым, как переопределение require.load(). Системы сборки это делают, метод require.load для этой среды можно найти в build/jslib/requirePatch.js.

В будущем этот код может быть перемещён в каталог require/ в качестве необязательного модуля, который можно загрузить в вашей среде, чтобы получить правильное поведение загрузки в зависимости от среды.

Параметры конфигурации

При использовании require() в HTML-странице верхнего уровня (или файле скрипта верхнего уровня, не определяющем модуль), объект конфигурации можно передать в качестве первого параметра:

<script src="scripts/require.js"></script>
<script>
  require.config({
    baseUrl: "/another/path",
    paths: {
        "some": "some/v1.0"
    },
    waitSeconds: 15
  });
  require( ["some/module", "my/module", "a.js", "b.js"],
    function(someModule,    myModule) {
        //This function will be called when all the dependencies
        //listed above are loaded. Note that this function could
        //be called before the page is loaded.
        //This callback is optional.
    }
  );
</script>

Вы также можете вызвать require.config из своей точки входа data-main, но имейте в виду, что скрипт data-main загружается асинхронно. Избегайте других точек входа, которые ошибочно предполагают, что data-main и его require.config всегда выполняются до загрузки их скрипта.

Также вы можете определить объект конфигурации как глобальную переменную require **до** загрузки require.js, и значения будут применены автоматически. В этом примере указаны некоторые зависимости, которые будут загружены сразу после того, как require.js определит require():

<script>
    var require = {
        deps: ["some/module1", "my/module2", "a.js", "b.js"],
        callback: function(module1, module2) {
            //This function will be called when all the dependencies
            //listed above in deps are loaded. Note that this
            //function could be called before the page is loaded.
            //This callback is optional.
        }
    };
</script>
<script src="scripts/require.js"></script>

Примечание: Лучше использовать var require = {} и не использовать window.require = {}, это не будет работать корректно в IE.

Существуют некоторые шаблоны для разделения конфигурации от загрузки основного модуля.

Поддерживаемые параметры конфигурации:

baseUrl: корневой путь для всех поисков модулей. Таким образом, в приведённом выше примере, скрипт «my/module» будет иметь src="/another/path/my/module.js". baseUrl **не** используется при загрузке обычных файлов .js (указано строкой зависимости, начинающейся с косой черты, имеющей протокол или заканчивающейся на .js), эти строки используются как есть, поэтому a.js и b.js будут загружены из того же каталога, что и HTML-страница, содержащая фрагмент.

Если baseUrl явно не задан в конфигурации, значение по умолчанию будет расположением HTML-страницы, которая загружает require.js. Если используется атрибут data-main, этот путь станет baseUrl.

BaseUrl может быть URL в другом домене, кроме страницы, которая загрузит require.js. Загрузка скриптов RequireJS работает между доменными зонами. Единственное ограничение связано с текстовым содержимым, загруженным плагинами text!: эти пути должны находиться в том же домене, что и страница, по крайней мере, во время разработки. Инструмент оптимизации встроит ресурсы плагинов text! Поэтому после использования инструмента оптимизации вы можете использовать ресурсы, которые ссылаются на ресурсы плагинов text! из другого домена.

Пути: сопоставление путей для имён модулей, которые не находятся непосредственно под baseUrl. Параметры пути предполагаются относительными к baseUrl, если путь в настройках не начинается с "/" или не содержит URL-протокола ("например, http:"). Используя приведенную выше конфигурацию, тег script для "some/module" будет иметь src="/another/path/some/v1.0/module.js".

Путь, используемый для имени модуля, не должен содержать расширение, так как сопоставление пути может относиться к каталогу. Код сопоставления путей автоматически добавит расширение .js при сопоставлении имени модуля с путём. Если используется require.toUrl(), будет добавлено соответствующее расширение, если это, например, текстовый шаблон.

При запуске в браузере можно указать сводные пути, чтобы разрешить попытку загрузки из расположения CDN, но возвратиться к локальному расположению, если загрузка из CDN не удалась.

Сборки: Введено в RequireJS 2.1.10: позволяет настроить несколько идентификаторов модулей, которые будут находиться в другом скрипте. Пример:

requirejs.config({
    bundles: {
        'primary': ['main', 'util', 'text', 'text!template.html'],
        'secondary': ['text!secondary.html']
    }
});

require(['util', 'text'], function(util, text) {
    //The script for module ID 'primary' was loaded,
    //and that script included the define()'d
    //modules for 'util' and 'text'
});

Эта конфигурация указывает, что модули 'main', 'util', 'text' и 'text!template.html' будут найдены путем загрузки идентификатора модуля 'primary'. Модуль 'text!secondary.html' может быть найден путем загрузки идентификатора модуля 'secondary'.

Это только задаёт, где найти модуль внутри скрипта, содержащего несколько модулей, определённых с помощью define(). Оно не автоматически связывает эти модули с идентификатором сборки модуля. Идентификатор сборки модуля используется только для определения набора модулей.

Аналогичное возможно с конфигурацией путей, но это гораздо более громоздко, и маршрут конфигурации путей не допускает идентификаторов ресурсов плагина загрузчика в его настройках, поскольку значения конфигурации путей являются сегментами путей, а не идентификаторами.

Конфигурация сборок полезна, если выполняется сборка, и целевой идентификатор модуля не является существующим, или если в скомпилированных JS-файлах имеются ресурсы плагинов загрузчика, которые не должны загружаться плагином загрузчика. Обратите внимание, что ключи и значения — это идентификаторы модулей, а не сегменты пути. Это абсолютные идентификаторы модулей, а не префикс идентификатора модуля, как в конфигурации путей или карты. Также конфигурация сборки отличается от конфигурации карты тем, что конфигурация карты представляет собой однозначное соотношение идентификаторов модулей, а конфигурация сборки служит для указания нескольких идентификаторов модулей на идентификатор сборки модуля.

Начиная с RequireJS 2.2.0, оптимизатор может сгенерировать конфигурацию сборок и вставить её в вызов requirejs.config() верхнего уровня. Дополнительные сведения см. в параметре конфигурации сборки bundlesConfigOutFile.

Настройка: Настройте зависимости, экспорт и пользовательную инициализацию для устаревших скриптов "глобальных переменных браузера", которые не используют define() для объявления зависимостей и установки значения модуля.

Вот пример. Требуется RequireJS 2.1.0+ и предполагается, что backbone.js, underscore.js и jquery.js установлены в каталоге baseUrl. Если нет, то может потребоваться настройка конфигурации путей для них:

requirejs.config({
    //Remember: only use shim config for non-AMD scripts,
    //scripts that do not already call define(). The shim
    //config will not work correctly if used on AMD scripts,
    //in particular, the exports and init config will not
    //be triggered, and the deps config will be confusing
    //for those cases.
    shim: {
        'backbone': {
            //These script dependencies should be loaded before loading
            //backbone.js
            deps: ['underscore', 'jquery'],
            //Once loaded, use the global 'Backbone' as the
            //module value.
            exports: 'Backbone'
        },
        'underscore': {
            exports: '_'
        },
        'foo': {
            deps: ['bar'],
            exports: 'Foo',
            init: function (bar) {
                //Using a function allows you to call noConflict for
                //libraries that support it, and do other cleanup.
                //However, plugins for those libraries may still want
                //a global. "this" for the function will be the global
                //object. The dependencies will be passed in as
                //function arguments. If this function returns a value,
                //then that value is used as the module export value
                //instead of the object found via the 'exports' string.
                //Note: jQuery registers as an AMD module via define(),
                //so this will not work for jQuery. See notes section
                //below for an approach for jQuery.
                return this.Foo.noConflict();
            }
        }
    }
});

//Then, later in a separate file, call it 'MyModel.js', a module is
//defined, specifying 'backbone' as a dependency. RequireJS will use
//the shim config to properly load 'backbone' and give a local
//reference to this module. The global Backbone will still exist on
//the page too.
define(['backbone'], function (Backbone) {
  return Backbone.Model.extend({});
});

В RequireJS 2.0.*, свойство "экспорт" в конфигурации настройки могло быть функцией вместо строки. В этом случае оно работало так же, как свойство "инициализация", как показано выше. Шаблон "инициализация" используется в RequireJS 2.1.0+, поэтому строковое значение для exports может использоваться для enforceDefine, но при этом позволяют функциональную работу, как только известно, что библиотека загружена.

Для "модулей", которые представляют собой только плагины jQuery или Backbone, которым не нужно экспортировать значение модуля, конфигурация shim может быть просто массивом зависимостей:

requirejs.config({
    shim: {
        'jquery.colorize': ['jquery'],
        'jquery.scroll': ['jquery'],
        'backbone.layoutmanager': ['backbone']
    }
});

Однако, если вы хотите получить обнаружение 404 ошибок в IE, чтобы можно было использовать сводные пути или обработчики ошибок, то должно быть указано строковое значение экспорта, чтобы загрузчик мог проверить, действительно ли скрипты загрузились (возврат от init не используется для enforceDefine проверки):

requirejs.config({
    shim: {
        'jquery.colorize': {
            deps: ['jquery'],
            exports: 'jQuery.fn.colorize'
        },
        'jquery.scroll': {
            deps: ['jquery'],
            exports: 'jQuery.fn.scroll'
        },
        'backbone.layoutmanager': {
            deps: ['backbone']
            exports: 'Backbone.LayoutManager'
        }
    }
});

Важные замечания для конфигурации "shim":

  • Конфигурация shim устанавливает только отношения кода. Для загрузки модулей, которые являются частью или используют конфигурацию shim, необходим обычный вызов require/define. Сама по себе настройка shim не запускает загрузку кода.
  • Используйте только другие "shim" модули в качестве зависимостей для скриптов shim, или AMD-библиотеки без зависимостей, которые вызывают define() после создания глобальной переменной (например, jQuery или lodash). Иначе, если вы используете AMD-модуль в качестве зависимости для модуля конфигурации shim, после сборки этот AMD-модуль может не быть оценен до выполнения кода shim в сборке, и произойдёт ошибка. Конечным решением является обновление всего кода shim для использования опциональных AMD define() вызовов.
  • Если невозможно обновить код shim для использования вызовов AMD define(), начиная с RequireJS 2.1.11, оптимизатор имеет параметр сборки wrapShim, который попытается автоматически обернуть код shim в define(). Это изменяет область зависимостей shim, поэтому гарантированно не всегда работает, но, например, для зависимостей shim, которые зависят от AMD-версии Backbone, может оказаться полезным.
  • Функция init не будет вызываться для AMD-модулей. Например, вы не можете использовать функцию shim init для вызова noConflict jQuery. См. Сопоставление модулей для использования noConflict для альтернативного подхода к jQuery.
  • Конфигурация shim не поддерживается при запуске AMD-модулей в узле через RequireJS (хотя она работает для использования оптимизатором). В зависимости от модуля shim, он может потерпеть неудачу в Node, поскольку Node не имеет той же глобальной среды, что и браузеры. Начиная с RequireJS 2.1.7, вы будете предупреждены в консоли, что конфигурация shim не поддерживается, и она может или не может работать. Если вы хотите подавить это сообщение, вы можете передать requirejs.config({ suppress: { nodeShim: true }});.

Важные замечания по оптимизации для конфигурации "shim":

  • Для указания файла, где находится конфигурация shim, необходимо использовать параметр mainConfigFile сборки. В противном случае оптимизатор не узнает о конфигурации shim. Другой вариант — дублировать конфигурацию shim в профиле сборки.
  • Не смешивайте загрузку CDN с конфигурацией shim в сборке. Пример сценария: вы загружаете jQuery из CDN, но используете конфигурацию shim для загрузки, например, стоковой версии Backbone, которая зависит от jQuery. При выполнении сборки убедитесь, что jQuery встроен в скомпилированный файл и не загружается из CDN. В противном случае Backbone будет встроен в скомпилированный файл и выполнится до загрузки загруженной из CDN jQuery. Это потому, что конфигурация shim просто откладывает загрузку файлов до загрузки зависимостей, но не выполняет автоматическое обертывание define(). После сборки зависимости уже внедрены, конфигурация shim не может отложить выполнение не-define() кода до более позднего времени. define() модули работают с кодом, загруженным из CDN после сборки, потому что они правильно заключают свой исходный код в функцию define factory, которая не будет выполняться до загрузки зависимостей. Итак, урок: конфигурация shim — это временная мера для немодульного кода, устаревшего кода. define() модули лучше.
  • Для локальных сборок из нескольких файлов вышеупомянутое предупреждение о CDN также применимо. Для любого скрипта shim, его зависимости должны быть загружены до выполнения скрипта shim. Это означает либо включение зависимостей непосредственно в слой сборки, который включает скрипт shim, либо загрузка зависимостей с помощью вызова require([], function (){}), а затем выполнение вложенного вызова require([]) для слоя сборки, содержащего скрипт shim.
  • Если вы используете uglifyjs для минификации кода, не устанавливайте опцию uglify toplevel в значение true, или если вы используете командную строку, не передавайте -mt. Этот параметр искажает имена глобальных переменных, которые shim использует для поиска экспорта.

Карта: Для данного префикса модуля вместо загрузки модуля с данным идентификатором подставляется другой идентификатор модуля.

Такая возможность очень важна для крупных проектов, которые могут иметь два набора модулей, которые должны использовать две разные версии 'foo', но всё ещё должны взаимодействовать друг с другом.

Это невозможно с помощью поддержки многоверсии на основе контекста. Кроме того, конфигурация путей предназначена только для настройки корневых путей для идентификаторов модулей, а не для сопоставления одного идентификатора модуля с другим.

Пример карты:

requirejs.config({
    map: {
        'some/newmodule': {
            'foo': 'foo1.2'
        },
        'some/oldmodule': {
            'foo': 'foo1.0'
        }
    }
});

Если модули расположены на диске следующим образом:

  • foo1.0.js
  • foo1.2.js
  • some/
    • newmodule.js
    • oldmodule.js

Когда 'some/newmodule' выполняет `require('foo')`, он получит файл foo1.2.js, а когда 'some/oldmodule' выполняет `require('foo')`, он получит файл foo1.0.js.

Эта функция хорошо работает только для реальных AMD-модулей, которые вызывают define() и регистрируются как анонимные модули. Кроме того, используйте только абсолютные идентификаторы модулей для конфигурации карты. Относительные идентификаторы (например, '../some/thing') не работают.

Также поддерживается значение карты "*", которое означает "для всех загруженных модулей используйте эту конфигурацию карты". Если существует более конкретная конфигурация карты, она будет иметь приоритет над конфигурацией звёздочки. Пример:

requirejs.config({
    map: {
        '*': {
            'foo': 'foo1.2'
        },
        'some/oldmodule': {
            'foo': 'foo1.0'
        }
    }
});

Это означает, что для любого модуля, кроме "some/oldmodule", при запросе "foo" использовать "foo1.2" вместо этого. Только для "some/oldmodule" использовать "foo1.0", когда он запрашивает "foo".

Примечание: при выполнении сборок с конфигурацией карты конфигурация карты должна быть передана оптимизатору, и выходной файл сборки должен по-прежнему содержать вызов requirejs config, который настраивает конфигурацию карты. Оптимизатор не переименовывает идентификаторы во время сборки, потому что некоторые ссылки на зависимости в проекте могут зависеть от состояния переменных во время выполнения. Таким образом, оптимизатор не делает ненужной конфигурацию карты после сборки.

Конфигурация: Существует общая потребность в передаче информации о конфигурации в модуль. Эта информация о конфигурации обычно известна как часть приложения, и необходимо иметь возможность передать её в модуль. В RequireJS это делается с помощью параметра config для requirejs.config(). Модули могут затем прочитать эту информацию, запросив специальную зависимость "module" и вызвав module.config(). Пример:

requirejs.config({
    config: {
        'bar': {
            size: 'large'
        },
        'baz': {
            color: 'blue'
        }
    }
});

//bar.js, which uses simplified CJS wrapping:
//http://requirejs.org/docs/whyamd.html#sugar
define(function (require, exports, module) {
    //Will be the value 'large'
    var size = module.config().size;
});

//baz.js which uses a dependency array,
//it asks for the special module ID, 'module':
//https://github.com/requirejs/requirejs/wiki/Differences-between-the-simplified-CommonJS-wrapper-and-standard-AMD-define#wiki-magic
define(['module'], function (module) {
    //Will be the value 'blue'
    var color = module.config().color;
});

Для передачи конфигурации в пакет, обратитесь к главному модулю в пакете, а не к идентификатору пакета:

requirejs.config({
    //Pass an API key for use in the pixie package's
    //main module.
    config: {
        'pixie/index': {
            apiKey: 'XJKDLNS'
        }
    },
    //Set up config for the "pixie" package, whose main
    //module is the index.js file in the pixie folder.
    packages: [
        {
            name: 'pixie',
            main: 'index'
        }
    ]
});

Пакеты: Настраивает загрузку модулей из пакетов CommonJS. Дополнительную информацию см. в разделе пакеты.

nodeIdCompat: Node рассматривает идентификатор модуля example.js и example одинаково. По умолчанию это два разных идентификатора в RequireJS. Если вы используете модули, установленные из npm, вам может потребоваться установить это значение конфигурации в true, чтобы избежать проблем с разрешением. Этот параметр применяется только к разному обращению с суффиксом ".js", он не выполняет другие соответствия разрешения и оценки узла, такие как обработка файлов .json (для обработки JSON всё равно нужен плагин загрузчика 'json!'). Доступно начиная с 2.1.10.

END_OF_DOCUMENT_MARKER

waitSeconds: Количество секунд ожидания перед отказом от загрузки скрипта. Установка значения 0 отключает таймаут. По умолчанию 7 секунд.

context: Имя загрузки контекста. Это позволяет require.js загружать несколько версий модулей на одной странице, при условии, что каждый вызов require верхнего уровня указывает уникальную строку контекста. Для правильного использования см. раздел Поддержка нескольких версий.

deps: Массив зависимостей для загрузки. Полезно, когда require определен как объект конфигурации до загрузки require.js, и вы хотите указать зависимости для загрузки как только require() будет определен. Использование deps аналогично вызову require([]), но выполняется сразу после обработки конфигурации загрузчиком. Он не блокирует другие вызовы require() от начала запросов модулей, а просто указывает некоторые модули для асинхронной загрузки в рамках блока конфигурации.

callback: Функция, которая выполняется после загрузки deps. Полезно, когда require определен как объект конфигурации до загрузки require.js, и вы хотите указать функцию для вызова после загрузки массива deps конфигурации.

enforceDefine: Если установлено в true, будет выброшено исключение, если загружен скрипт, который не вызывает define() или не имеет значения shim exports, которое можно проверить. Дополнительная информация в разделе Обработка ошибок загрузки в IE.

xhtml: Если установлено в true, для создания элементов скрипта будет использоваться document.createElementNS().

urlArgs: Дополнительные параметры строки запроса, добавляемые к URL, которые RequireJS использует для получения ресурсов. Наиболее полезно для "cache busting" (обхода кэша) в случае неправильной конфигурации браузера или сервера. Пример настройки "cache busting" для urlArgs:

urlArgs: "bust=" +  (new Date()).getTime()

Начиная с RequireJS 2.2.0, urlArgs может быть функцией. Если это функция, она получит идентификатор модуля и URL в качестве параметров и должна вернуть строку, которая будет добавлена в конец URL. Верните пустую строку, если не нужно добавлять аргументы. Убедитесь, что вы добавляете «?» или «&» в зависимости от текущего состояния URL. Пример:

requirejs.config({
    urlArgs: function(id, url) {
        var args = 'v=1';
        if (url.indexOf('view.html') !== -1) {
            args = 'v=2'
        }

        return (url.indexOf('?') === -1 ? '?' : '&') + args;
    }
});

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

scriptType: Укажите значение атрибута type для тегов script, вставленных в документ RequireJS. По умолчанию "text/javascript". Для использования возможностей JavaScript 1.8 в Firefox используйте "text/javascript;version=1.8".

skipDataMain: Введено в RequireJS 2.1.9: Если установлено в true, пропускается сканирование атрибута data-main для запуска загрузки модулей. Полезно, если RequireJS встроен в библиотеку утилит, которая может взаимодействовать с другими библиотеками RequireJS на странице, и встроенная версия не должна выполнять загрузку data-main.

Расширенное использование

Загрузка модулей из пакетов

RequireJS поддерживает загрузку модулей, находящихся в структуре каталогов CommonJS Packages, но для ее работы требуется дополнительная настройка. В частности, поддерживаются следующие функции CommonJS Packages:

  • Пакет может быть связан с именем/префиксом модуля.
  • Конфигурация пакета может содержать следующие свойства для конкретного пакета:
    • name: Название пакета (используется для сопоставления имени/префикса модуля)
    • location: Путь к папке на диске. Пути относительны к значению конфигурации baseUrl, если они не содержат протокол или не начинаются с косой черты (/).
    • main: Имя модуля внутри пакета, которое должно использоваться при require для "packageName". Значение по умолчанию – "main", поэтому указывайте его только если оно отличается. Значение относительно папки пакета.

ВАЖНЫЕ ПРИМЕЧАНИЯ

  • Хотя пакеты могут иметь структуру каталогов CommonJS, сами модули должны быть в формате, понятном RequireJS. Исключение: если вы используете адаптер r.js Node, модули могут быть в традиционном формате модулей CommonJS. Вы можете использовать инструмент преобразования CommonJS, если вам нужно преобразовать традиционные модули CommonJS в асинхронный формат, используемый RequireJS.
  • В одном проекте может использоваться только одна версия пакета. Вы можете использовать поддержку нескольких версий RequireJS для загрузки двух разных контекстов модулей, но если вы хотите использовать пакет А и Б в одном контексте, и они зависят от разных версий пакета С, то это может вызвать проблемы. Возможно, в будущем это изменится.

Если вы используете структуру проекта, аналогичную той, что описана в Руководстве по началу работы, то начало вашего веб-проекта будет выглядеть примерно так (проекты, основанные на Node/Rhino, аналогичны, просто используйте содержимое каталога scripts как корневой каталог проекта):

  • каталог-проекта/
    • project.html
    • scripts/
      • require.js

Вот как пример структуры каталогов выглядит с двумя пакетами, cart и store:

  • каталог-проекта/
    • project.html
    • scripts/
      • cart/
        • main.js
      • store/
        • main.js
        • util.js
      • main.js
      • require.js

project.html будет содержать тег script, подобный этому:

<script data-main="scripts/main" src="scripts/require.js"></script>

Это указывает require.js загрузить скрипт scripts/main.js. main.js использует конфигурацию "packages", чтобы настроить пакеты, которые относительны к require.js, а в данном случае – это исходные пакеты "cart" и "store":

//main.js contents
//Pass a config object to require
require.config({
    "packages": ["cart", "store"]
});

require(["cart", "store", "store/util"],
function (cart,   store,   util) {
    //use the modules as usual.
});

require "cart" означает, что он будет загружен из scripts/cart/main.js, так как "main" – это значение по умолчанию для модуля main, поддерживаемое RequireJS. require "store/util" будет загружен из scripts/store/util.js.

Если пакет "store" не следовал соглашению "main.js", а выглядел так:

  • каталог-проекта/
    • project.html
    • scripts/
      • cart/
        • main.js
      • store/
        • store.js
        • util.js
      • main.js
      • package.json
      • require.js

Тогда конфигурация RequireJS будет выглядеть так:

require.config({
    packages: [
        "cart",
        {
            name: "store",
            main: "store"
        }
    ]
});

Для избежания многословия, настоятельно рекомендуется всегда использовать пакеты, использующие соглашение "main" в своей структуре.

Поддержка нескольких версий

Как упоминалось в разделе Параметры конфигурации, несколько версий модуля могут быть загружены на одной странице, используя различные параметры конфигурации "context". require.config() возвращает функцию require, которая будет использовать параметры конфигурации context. Вот пример, который загружает две разные версии модулей alpha и beta (этот пример взят из одного из тестовых файлов):

<script src="../require.js"></script>
<script>
var reqOne = require.config({
  context: "version1",
  baseUrl: "version1"
});

reqOne(["require", "alpha", "beta",],
function(require,   alpha,   beta) {
  log("alpha version is: " + alpha.version); //prints 1
  log("beta version is: " + beta.version); //prints 1

  setTimeout(function() {
    require(["omega"],
      function(omega) {
        log("version1 omega loaded with version: " +
             omega.version); //prints 1
      }
    );
  }, 100);
});

var reqTwo = require.config({
      context: "version2",
      baseUrl: "version2"
    });

reqTwo(["require", "alpha", "beta"],
function(require,   alpha,   beta) {
  log("alpha version is: " + alpha.version); //prints 2
  log("beta version is: " + beta.version); //prints 2

  setTimeout(function() {
    require(["omega"],
      function(omega) {
        log("version2 omega loaded with version: " +
            omega.version); //prints 2
      }
    );
  }, 100);
});
</script>

Обратите внимание, что "require" указан в качестве зависимости для модуля. Это позволяет функции require(), которая передается в функцию обратного вызова, использовать правильный контекст для корректной загрузки модулей в поддержке нескольких версий. Если "require" не указан как зависимость, то, скорее всего, произойдет ошибка.

Загрузка кода после загрузки страницы

Пример выше в разделе Поддержка нескольких версий демонстрирует, как код может быть загружен позже с помощью вложенных вызовов require().

Поддержка Web Worker

Начиная с версии 0.12, RequireJS может работать внутри Web Worker. Просто используйте importScripts() внутри web worker для загрузки require.js (или JS файла, содержащего определение require()), а затем вызовите require.

Вероятно, вам нужно установить параметр конфигурации baseUrl для обеспечения возможности для require() находить загружаемые скрипты.

Пример его использования можно найти в одном из файлов, используемых в тестовом наборе.

Поддержка Rhino

RequireJS может использоваться в Rhino через адаптер r.js. Подробная информация в README r.js.

Поддержка Nashorn

Начиная с RequireJS 2.1.16, RequireJS может использоваться в Nashorn, JavaScript-движке Java 8+, через адаптер r.js. Подробная информация в README r.js.

Обработка ошибок

Общий класс ошибок – 404 для скриптов (не найден), сетевые таймауты или ошибки в загружаемых скриптах. RequireJS предоставляет несколько инструментов для их обработки: require-специфические errbacks, массив конфигурации "paths" и глобальную функцию requirejs.onError.

Объект ошибки, переданный в errbacks и функцию requirejs.onError, обычно содержит два пользовательских свойства:

  • requireType: Строковое значение с общей классификацией, например, "timeout", "nodefine", "scripterror".
  • requireModules: Массив имен/URL модулей, которые завершились таймаутом.

Если у вас возникла ошибка с requireModules, это, вероятно, означает, что другие модули, зависящие от модулей в массиве requireModules, не определены.

Обработка ошибок загрузки в IE

Internet Explorer имеет ряд проблем, которые затрудняют обнаружение ошибок загрузки для errbacks/paths fallbacks:

  • script.onerror не работает в IE 6-8. Нет способа узнать, если загрузка скрипта вызывает 404, хуже того, он вызывает onreadystatechange со статусом complete даже в случае 404.
  • script.onerror работает в IE 9+, но имеет ошибку, где он не запускает обработчики script.onload сразу после выполнения скрипта, поэтому он не может поддерживать стандартный метод разрешения анонимных AMD-модулей. Поэтому script.onreadystatechange всё ещё используется. Однако onreadystatechange срабатывает со статусом complete до того, как сработает функция script.onerror.

Таким образом, в IE очень трудно разрешить как анонимные AMD-модули, которые являются ключевым преимуществом AMD-модулей, так и надежно обнаруживать ошибки.

Однако, если ваш проект гарантированно использует define() для объявления всех модулей или использует конфигурацию shim для указания строк экспорта для всех, кто не использует define(), то если вы установите значение конфигурации enforceDefine в true, загрузчик может проверить загрузку скрипта, проверяя вызов define() или существование глобального значения экспорта shim.

END_OF_DOCUMENT_MARKER

Таким образом, если вы хотите поддерживать Internet Explorer, перехватывать ошибки загрузки и иметь модульный код, либо через непосредственные вызовы define(), либо с помощью конфигурации shim, всегда устанавливайте enforceDefine в значение true. Пример см. в следующем разделе.

ПРИМЕЧАНИЕ: Если вы установили enforceDefine: true и используете data-main="" для загрузки своего основного модуля JS, то этот основной модуль JS должен вызывать define() вместо require() для загрузки необходимых ему кодов. Основной модуль JS по-прежнему может вызывать require/requirejs для настройки значений конфигурации, но для загрузки модулей он должен использовать define().

Если вы также используете almond для построения кода без require.js, убедитесь, что используете настройку сборки insertRequire для вставки вызова require для основного модуля — это служит той же цели, что и начальный вызов require(), который выполняет data-main.

Обработка ошибок require([])

Обработчики ошибок, когда используются с requirejs.undef(), позволят вам обнаруживать, если модуль не загружается, удалять этот модуль, сбросить конфигурацию в другое место, а затем попробовать снова.

Распространенный случай использования этого — использование CDN-версии библиотеки, но если это не удается, переключиться на загрузку файла локально:

requirejs.config({
    enforceDefine: true,
    paths: {
        jquery: 'http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min'
    }
});

//Later
require(['jquery'], function ($) {
    //Do something with $ here
}, function (err) {
    //The errback, error callback
    //The error has a list of modules that failed
    var failedId = err.requireModules && err.requireModules[0];
    if (failedId === 'jquery') {
        //undef is function only on the global requirejs object.
        //Use it to clear internal knowledge of jQuery. Any modules
        //that were dependent on jQuery and in the middle of loading
        //will not be loaded yet, they will wait until a valid jQuery
        //does load.
        requirejs.undef(failedId);

        //Set the path to jQuery to local path
        requirejs.config({
            paths: {
                jquery: 'local/jquery'
            }
        });

        //Try again. Note that the above require callback
        //with the "Do something with $ here" comment will
        //be called if this new attempt to load jQuery succeeds.
        require(['jquery'], function () {});
    } else {
        //Some other error. Maybe show message to the user.
    }
});

С помощью `requirejs.undef()`, если вы позже настроите другую конфигурацию и попытаетесь загрузить тот же модуль, загрузчик все равно будет помнить, какие модули нуждались в этой зависимости, и завершит их загрузку, когда загрузится вновь настроенный модуль.

Примечание: обработчики ошибок работают только с вызовами require в стиле обратного вызова, а не с вызовами define(). define() используется только для объявления модулей.

Падение конфигурации paths

Приведенный выше шаблон для обнаружения сбоя загрузки, удаления модуля с помощью undef(), изменения путей и перезагрузки достаточно распространен, что для него есть и сокращенная запись. Конфигурация paths позволяет использовать массивы значений:

requirejs.config({
    //To get timely, correct error triggers in IE, force a define/shim exports check.
    enforceDefine: true,
    paths: {
        jquery: [
            'http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min',
            //If the CDN location fails, load from this location
            'lib/jquery'
        ]
    }
});

//Later
require(['jquery'], function ($) {
});

Этот код попытается загрузить модуль с CDN-местоположения, но если это не удастся, перейдет к локальному местоположению lib/jquery.js.

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

Глобальная функция requirejs.onError

Для обнаружения ошибок, которые не обрабатываются локальными обработчиками ошибок, можно переопределить requirejs.onError():

requirejs.onError = function (err) {
    console.log(err.requireType);
    if (err.requireType === 'timeout') {
        console.log('modules: ' + err.requireModules);
    }

    throw err;
};

Плагины загрузчика

RequireJS поддерживает плагины загрузчика. Это способ поддержки зависимостей, которые не являются обычными JS-файлами, но все же важны для загрузки скрипта перед выполнением его работы. В вики RequireJS есть список плагинов. Этот раздел описывает некоторые конкретные плагины, которые поддерживаются вместе с RequireJS:

Укажите зависимость файла текста

Приятно создавать HTML с помощью обычных тегов HTML вместо построения структур DOM в скрипте. Однако нет хорошего способа встраивать HTML в файл JavaScript. Лучшее, что можно сделать, это использовать строку HTML, но это может быть сложно для управления, особенно для многострочного HTML.

RequireJS имеет плагин text.js, который может помочь в этой проблеме. Он автоматически загрузится, если для зависимости используется префикс text!. Смотрите README text.js для получения дополнительной информации.

Поддержка события загрузки страницы/готовности DOM

Возможна ситуация при использовании RequireJS, когда скрипты загружаются достаточно быстро, что завершаются до готовности DOM. Любая работа, пытающаяся взаимодействовать с DOM, должна ждать готовности DOM.

Однако не все используемые браузеры поддерживают событие DOMContentLoaded. Модуль domReady реализует кроссбраузерный метод определения готовности DOM. Загрузите модуль и используйте его в вашем проекте следующим образом:

require(['domReady'], function (domReady) {
  domReady(function () {
    //This function is called once the DOM is ready.
    //It will be safe to query the DOM and manipulate
    //DOM nodes in this function.
  });
});

Поскольку готовность DOM — распространенная потребность в приложении, в идеале вложенные функции в приведенном выше API можно было бы избежать. Модуль domReady также реализует API плагина загрузчика, поэтому вы можете использовать синтаксис плагина загрузчика (обратите внимание на ! в зависимости domReady), чтобы заставить функцию обратного вызова require() ждать готовности DOM перед выполнением.

domReady
вернет текущий документ при использовании в качестве плагина загрузчика:
require(['domReady!'], function (doc) {
    //This function is called once the DOM is ready,
    //notice the value for 'domReady!' is the current
    //document.
});

Примечание: Если документ загружается долго (возможно, это очень большой документ или содержит теги HTML-скриптов, загружающие большие JS-файлы, которые блокируют завершение DOM до их завершения), использование domReady в качестве плагина загрузчика может привести к ошибке «timeout» RequireJS. Если это проблема, либо увеличьте конфигурацию waitSeconds, либо просто используйте domReady как модуль и вызовите domReady() внутри обратного вызова require().

Определите пакет i18n

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

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

Поддержка пакетов i18n предоставляется плагином i18n.js. Он автоматически загружается, когда модуль или зависимость указывают префикс i18n! (подробнее ниже). Загрузите плагин и поместите его в ту же папку, что и основной JS-файл вашего приложения.

Чтобы определить пакет, поместите его в папку «nls» — плагин i18n! предполагает, что имя модуля с «nls» в нем указывает на пакет i18n. Маркер «nls» в имени указывает плагину i18n, где ожидать папки с языками (они должны быть непосредственными потомками папки nls). Если вы хотите предоставить набор имён цветов в вашем наборе модулей «my», создайте структуру каталогов следующим образом:

  • my/nls/colors.js

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

//my/nls/colors.js contents:
define({
    "root": {
        "red": "red",
        "blue": "blue",
        "green": "green"
    }
});

Объектная запись со свойством «root» определяет этот модуль. Это все, что вам нужно сделать, чтобы подготовиться к последующей работе по локализации.

Затем вы можете использовать указанный выше модуль в другом модуле, например, в файле my/lamps.js:

//Contents of my/lamps.js
define(["i18n!my/nls/colors"], function(colors) {
    return {
        testMessage: "The name for red in this locale is: " + colors.red
    }
});

Модуль my/lamps имеет одно свойство с именем «testMessage», которое использует colors.red для отображения локализованного значения для цвета красного.

Позже, когда вы захотите добавить определенный перевод в файл, например для языка fr-fr, измените my/nls/colors следующим образом:

//Contents of my/nls/colors.js
define({
    "root": {
        "red": "red",
        "blue": "blue",
        "green": "green"
    },
    "fr-fr": true
});

Затем определите файл в my/nls/fr-fr/colors.js с содержанием:

//Contents of my/nls/fr-fr/colors.js
define({
    "red": "rouge",
    "blue": "bleu",
    "green": "vert"
});

RequireJS будет использовать свойство браузера navigator.languages, navigator.language или navigator.userLanguage для определения значений языков для my/nls/colors, поэтому вашему приложению не нужно ничего менять. Если вы предпочитаете задавать язык, вы можете использовать конфигурацию модуля для передачи языка в плагин:

requirejs.config({
    config: {
        //Set the config for the i18n
        //module ID
        i18n: {
            locale: 'fr-fr'
        }
    }
});

Примечание, что RequireJS всегда будет использовать строчную версию языка, чтобы избежать проблем с регистром, поэтому все каталоги и файлы на диске для пакетов i18n должны использовать строчные языки.

RequireJS также достаточно умен, чтобы выбрать правильный пакет локализации, наиболее точно соответствующий предоставленным my/nls/colors. Например, если язык «en-us», будет использоваться пакет «root». Если язык «fr-fr-paris», будет использоваться пакет «fr-fr».

RequireJS также объединяет пакеты вместе, поэтому, например, если французский пакет был определён таким образом (опуская значение для red):

//Contents of my/nls/fr-fr/colors.js
define({
    "blue": "bleu",
    "green": "vert"
});

Тогда значение для red в «root» будет использоваться. Это работает для всех частей локализации. Если были определены все перечисленные ниже пакеты, то RequireJS будет использовать значения в следующем порядке приоритетов (пакет вверху имеет наибольший приоритет):

  • my/nls/fr-fr-paris/colors.js
  • my/nls/fr-fr/colors.js
  • my/nls/fr/colors.js
  • my/nls/colors.js

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

//my/nls/colors.js contents:
define({
    "root": true,
    "fr-fr": true,
    "fr-fr-paris": true
});

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

//Contents of my/nls/root/colors.js
define({
    "red": "red",
    "blue": "blue",
    "green": "green"
});

© jQuery Foundation and other contributors
Licensed under the MIT License.
http://requirejs.org/docs/api.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API