Подтвердить что ты не робот

Динамическое требование в RequireJS, так как ошибка "Ошибка имени модуля еще не загружена для контекста"?

Есть ли способ определить модуль, который "динамически" загружает другие модули в RequireJS? Если да, то как оптимизатор (r.js) понимает, как/когда модуль должен быть включен?

Например, пусть dynModules модуль, который определяет пары имя/путь:

define([], function () {
    return ['moduleA', 'moduleB']; // Array of module names
});

Другой модуль будет загружать модули динамически, основываясь на массиве. Это будет не работать:

define(['dyn_modules'], function (dynModules) {
    for(name in dynModules) {   
        var module = require(path); // Call RequireJS require
    }

    // ...
});

... дает мне:

Неисправленная ошибка: имя модуля "moduleA" еще не загружено для контекст: _. Используйте require ([]) http://requirejs.org/docs/errors.html#notloaded

Я могу решить ошибку, но она больше не "динамическая":

define(['dyn_modules', 'moduleA', 'moduleB'], function (dynModules) {
    for(name in dynModules) {   
        var module = require(path); // Call RequireJS require
    }

    // ...
});
4b9b3361

Ответ 1

Ограничение относится к упрощенному синтаксису CommonJS против стандартного синтаксиса обратного вызова:

Загрузка модуля по сути является асинхронным процессом из-за неизвестного времени его загрузки. Однако RequireJS в эмуляции серверной спецификации CommonJS пытается дать вам упрощенный синтаксис. Когда вы выполните что-то вроде этого:

var foomodule = require('foo');
// do something with fooModule

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

var module = require(path); // Call RequireJS require

... then Require не может разобрать это и автоматически преобразовать его. Решением является преобразование в синтаксис обратного вызова;

var moduleName = 'foo';
require([moduleName], function(fooModule){
    // do something with fooModule
})

Учитывая приведенное выше, вот один из возможных вариантов повторения вашего второго примера для использования стандартного синтаксиса:

define(['dyn_modules'], function (dynModules) {
    require(dynModules, function(){
        // use arguments since you don't know how many modules you're getting in the callback
        for (var i = 0; i < arguments.length; i++){
            var mymodule = arguments[i];
            // do something with mymodule...
        }
    });

});

EDIT: из вашего собственного ответа, я вижу, что вы используете underscore/lodash, поэтому использование _.values и _.object может упростить цикл с использованием массива аргументов, как указано выше.

Ответ 2

Отвечая на вопрос. С сайта RequireJS:

//THIS WILL FAIL
define(['require'], function (require) {
    var namedModule = require('name');
});

Это не выполняется, потому что requirejs обязательно должен загружать и выполнять все зависимости перед вызовом функции factory выше. [...] Итак, либо не переходите в массив зависимостей, либо, если используете массив зависимостей, перечислите все зависимости в нем.

Мое решение:

// Modules configuration (modules that will be used as Jade helpers)
define(function () {
    return {
        'moment':   'path/to/moment',
        'filesize': 'path/to/filesize',
        '_':        'path/to/lodash',
        '_s':       'path/to/underscore.string'
    };
});

Загрузчик:

define(['jade', 'lodash', 'config'], function (Jade, _, Config) {
    var deps;

    // Dynamic require
    require(_.values(Config), function () {
        deps = _.object(_.keys(Config), arguments);

        // Use deps...
    });
});